0
# Telemetry & Logging
1
2
Telemetry and logging functionality for monitoring bot performance, usage analytics, and debugging. Includes telemetry clients, logging middleware, and transcript storage.
3
4
## Capabilities
5
6
### BotTelemetryClient
7
8
Abstract base class for telemetry clients that defines the interface for tracking bot events, exceptions, and custom metrics for monitoring and analytics.
9
10
```python { .api }
11
class BotTelemetryClient:
12
def track_event(self, name: str, properties: dict = None, measurements: dict = None):
13
"""
14
Track custom telemetry events.
15
16
Args:
17
name (str): Event name
18
properties (dict, optional): Event properties
19
measurements (dict, optional): Event measurements
20
"""
21
22
def track_exception(self, exception, properties: dict = None, measurements: dict = None):
23
"""
24
Track exceptions for monitoring.
25
26
Args:
27
exception (Exception): Exception to track
28
properties (dict, optional): Additional properties
29
measurements (dict, optional): Additional measurements
30
"""
31
32
def track_dependency(self, name: str, data: str, type_name: str, target: str, duration: int, success: bool, result_code: str = None, properties: dict = None, measurements: dict = None):
33
"""Track dependency calls."""
34
35
def track_trace(self, name: str, properties: dict = None, severity=None):
36
"""Track trace messages."""
37
38
def track_page_view(self, name: str, url: str = None, duration: int = None, properties: dict = None, measurements: dict = None):
39
"""Track page views."""
40
41
def flush(self):
42
"""Flush telemetry data."""
43
```
44
45
### NullTelemetryClient
46
47
No-operation telemetry client that provides a default implementation for scenarios where telemetry is not needed or configured.
48
49
```python { .api }
50
class NullTelemetryClient(BotTelemetryClient):
51
def track_event(self, name: str, properties: dict = None, measurements: dict = None):
52
"""No-op track event implementation."""
53
54
def track_exception(self, exception, properties: dict = None, measurements: dict = None):
55
"""No-op track exception implementation."""
56
57
def track_dependency(self, name: str, data: str, type_name: str, target: str, duration: int, success: bool, result_code: str = None, properties: dict = None, measurements: dict = None):
58
"""No-op track dependency implementation."""
59
60
def track_trace(self, name: str, properties: dict = None, severity=None):
61
"""No-op track trace implementation."""
62
63
def track_page_view(self, name: str, url: str = None, duration: int = None, properties: dict = None, measurements: dict = None):
64
"""No-op track page view implementation."""
65
66
def flush(self):
67
"""No-op flush implementation."""
68
```
69
70
### TranscriptLogger
71
72
Abstract base class for transcript logging that defines the interface for storing conversation transcripts for audit and debugging purposes.
73
74
```python { .api }
75
class TranscriptLogger:
76
async def log_activity(self, activity):
77
"""
78
Log activity to transcript store.
79
80
Args:
81
activity (Activity): Activity to log
82
"""
83
84
async def get_transcript_activities(self, channel_id: str, conversation_id: str, continuation_token: str = None, start_date = None):
85
"""
86
Get transcript activities from store.
87
88
Args:
89
channel_id (str): Channel ID
90
conversation_id (str): Conversation ID
91
continuation_token (str, optional): Continuation token for paging
92
start_date (optional): Start date filter
93
94
Returns:
95
PagedResult: Paged transcript activities
96
"""
97
98
async def list_transcripts(self, channel_id: str, continuation_token: str = None):
99
"""
100
List available transcripts.
101
102
Args:
103
channel_id (str): Channel ID
104
continuation_token (str, optional): Continuation token for paging
105
106
Returns:
107
PagedResult: Paged transcript summaries
108
"""
109
110
async def delete_transcript(self, channel_id: str, conversation_id: str):
111
"""
112
Delete transcript.
113
114
Args:
115
channel_id (str): Channel ID
116
conversation_id (str): Conversation ID
117
"""
118
```
119
120
### MemoryTranscriptStore
121
122
In-memory implementation of transcript logger for development and testing scenarios where persistent transcript storage is not required.
123
124
```python { .api }
125
class MemoryTranscriptStore(TranscriptLogger):
126
def __init__(self):
127
"""Initialize memory transcript store."""
128
129
async def log_activity(self, activity):
130
"""Log activity to memory store."""
131
132
async def get_transcript_activities(self, channel_id: str, conversation_id: str, continuation_token: str = None, start_date = None):
133
"""Get transcript activities from memory."""
134
135
async def list_transcripts(self, channel_id: str, continuation_token: str = None):
136
"""List transcripts from memory."""
137
138
async def delete_transcript(self, channel_id: str, conversation_id: str):
139
"""Delete transcript from memory."""
140
```
141
142
### Severity Enum
143
144
Enumeration of telemetry severity levels for categorizing telemetry events and traces.
145
146
```python { .api }
147
class Severity:
148
VERBOSE = 0
149
INFORMATION = 1
150
WARNING = 2
151
ERROR = 3
152
CRITICAL = 4
153
```
154
155
### Telemetry Constants
156
157
Constants used throughout the telemetry system for standard property names and event types.
158
159
```python { .api }
160
class TelemetryConstants:
161
ACTIVITY_ID_PROPERTY = "activityId"
162
ACTIVITY_TYPE_PROPERTY = "activityType"
163
CHANNEL_ID_PROPERTY = "channelId"
164
CONVERSATION_ID_PROPERTY = "conversationId"
165
CONVERSATION_NAME_PROPERTY = "conversationName"
166
DIALOG_ID_PROPERTY = "dialogId"
167
FROM_ID_PROPERTY = "fromId"
168
FROM_NAME_PROPERTY = "fromName"
169
LOCALE_PROPERTY = "locale"
170
RECIPIENT_ID_PROPERTY = "recipientId"
171
RECIPIENT_NAME_PROPERTY = "recipientName"
172
REPLY_ACTIVITY_ID_PROPERTY = "replyActivityId"
173
TEXT_PROPERTY = "text"
174
SPEAK_PROPERTY = "speak"
175
USER_ID_PROPERTY = "userId"
176
```
177
178
## Usage Examples
179
180
### Basic Telemetry Setup
181
182
```python
183
from botbuilder.core import ActivityHandler, TurnContext, BotTelemetryClient
184
185
class TelemetryBot(ActivityHandler):
186
def __init__(self, telemetry_client: BotTelemetryClient):
187
self.telemetry_client = telemetry_client
188
189
async def on_message_activity(self, turn_context: TurnContext):
190
# Track message received event
191
self.telemetry_client.track_event(
192
"MessageReceived",
193
properties={
194
"conversationId": turn_context.activity.conversation.id,
195
"userId": turn_context.activity.from_property.id,
196
"text": turn_context.activity.text[:100] # Truncate for privacy
197
}
198
)
199
200
try:
201
# Process message
202
response = await self.process_message(turn_context.activity.text)
203
await turn_context.send_activity(MessageFactory.text(response))
204
205
# Track successful response
206
self.telemetry_client.track_event(
207
"MessageProcessed",
208
properties={
209
"conversationId": turn_context.activity.conversation.id,
210
"success": "true"
211
}
212
)
213
214
except Exception as e:
215
# Track exceptions
216
self.telemetry_client.track_exception(
217
e,
218
properties={
219
"conversationId": turn_context.activity.conversation.id,
220
"userId": turn_context.activity.from_property.id
221
}
222
)
223
await turn_context.send_activity(MessageFactory.text("Sorry, an error occurred"))
224
225
async def process_message(self, text: str):
226
# Simulate some processing
227
import time
228
start_time = time.time()
229
230
# Track dependency call (e.g., to external API)
231
try:
232
result = await self.call_external_api(text)
233
duration = int((time.time() - start_time) * 1000)
234
235
self.telemetry_client.track_dependency(
236
name="ExternalAPI",
237
data=f"ProcessText: {text[:50]}",
238
type_name="HTTP",
239
target="api.example.com",
240
duration=duration,
241
success=True,
242
result_code="200"
243
)
244
245
return result
246
247
except Exception as e:
248
duration = int((time.time() - start_time) * 1000)
249
250
self.telemetry_client.track_dependency(
251
name="ExternalAPI",
252
data=f"ProcessText: {text[:50]}",
253
type_name="HTTP",
254
target="api.example.com",
255
duration=duration,
256
success=False,
257
result_code="500"
258
)
259
raise
260
```
261
262
### Transcript Logging
263
264
```python
265
from botbuilder.core import MemoryTranscriptStore, TranscriptLoggerMiddleware
266
267
class TranscriptBot(ActivityHandler):
268
def __init__(self):
269
# Create transcript store
270
self.transcript_store = MemoryTranscriptStore()
271
272
# Add transcript logging middleware to adapter
273
self.adapter.use(TranscriptLoggerMiddleware(self.transcript_store))
274
275
async def on_message_activity(self, turn_context: TurnContext):
276
# Regular bot processing - transcript logging happens automatically
277
await turn_context.send_activity(MessageFactory.text(f"You said: {turn_context.activity.text}"))
278
279
async def get_conversation_history(self, channel_id: str, conversation_id: str):
280
"""Get conversation transcript."""
281
transcript = await self.transcript_store.get_transcript_activities(
282
channel_id,
283
conversation_id
284
)
285
return transcript.items
286
```
287
288
### Custom Telemetry Client
289
290
```python
291
import logging
292
293
class CustomTelemetryClient(BotTelemetryClient):
294
def __init__(self, logger=None):
295
self.logger = logger or logging.getLogger(__name__)
296
297
def track_event(self, name: str, properties: dict = None, measurements: dict = None):
298
"""Custom event tracking implementation."""
299
log_data = {
300
"event": name,
301
"properties": properties or {},
302
"measurements": measurements or {}
303
}
304
self.logger.info(f"Event: {log_data}")
305
306
# Send to your analytics service
307
# await self.send_to_analytics_service(log_data)
308
309
def track_exception(self, exception, properties: dict = None, measurements: dict = None):
310
"""Custom exception tracking."""
311
log_data = {
312
"exception": str(exception),
313
"type": type(exception).__name__,
314
"properties": properties or {}
315
}
316
self.logger.error(f"Exception: {log_data}")
317
318
# Send to your error tracking service
319
# await self.send_to_error_service(log_data)
320
321
def track_trace(self, name: str, properties: dict = None, severity=None):
322
"""Custom trace tracking."""
323
level = logging.INFO
324
if severity == Severity.WARNING:
325
level = logging.WARNING
326
elif severity == Severity.ERROR:
327
level = logging.ERROR
328
elif severity == Severity.CRITICAL:
329
level = logging.CRITICAL
330
331
self.logger.log(level, f"Trace: {name}", extra=properties or {})
332
333
def flush(self):
334
"""Flush telemetry data."""
335
# Implement flushing logic for your telemetry backend
336
pass
337
```
338
339
### Performance Metrics
340
341
```python
342
import time
343
from botbuilder.core import Middleware
344
345
class PerformanceTelemetryMiddleware(Middleware):
346
def __init__(self, telemetry_client: BotTelemetryClient):
347
self.telemetry_client = telemetry_client
348
349
async def on_turn(self, turn_context: TurnContext, next_middleware):
350
start_time = time.time()
351
352
try:
353
await next_middleware()
354
355
# Track successful turn
356
duration = (time.time() - start_time) * 1000
357
self.telemetry_client.track_event(
358
"TurnCompleted",
359
properties={
360
"conversationId": turn_context.activity.conversation.id,
361
"activityType": turn_context.activity.type
362
},
363
measurements={
364
"durationMs": duration
365
}
366
)
367
368
except Exception as e:
369
# Track failed turn
370
duration = (time.time() - start_time) * 1000
371
self.telemetry_client.track_event(
372
"TurnFailed",
373
properties={
374
"conversationId": turn_context.activity.conversation.id,
375
"error": str(e)
376
},
377
measurements={
378
"durationMs": duration
379
}
380
)
381
raise
382
```
383
384
## Types
385
386
```python { .api }
387
class PagedResult:
388
"""Paged result for transcript queries."""
389
def __init__(self):
390
self.items: list = []
391
self.continuation_token: str = None
392
393
class TranscriptInfo:
394
"""Information about a transcript."""
395
def __init__(self):
396
self.channel_id: str = None
397
self.id: str = None
398
self.created: str = None
399
```