Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'package:fixnum/fixnum.dart';
5 :
6 : import '../../dartastic_opentelemetry.dart';
7 :
8 : /// A read-only view of a log record.
9 : ///
10 : /// This interface provides read-only access to all LogRecord information
11 : /// for exporters and processors that only need to read the data.
12 : ///
13 : /// Implementations can access InstrumentationScope and Resource implicitly.
14 : ///
15 : /// More information:
16 : /// https://opentelemetry.io/docs/specs/otel/logs/sdk/#readablelogrecord
17 : abstract class ReadableLogRecord implements LogRecord {
18 : /// The instrumentation scope associated with this log record.
19 : InstrumentationScope get instrumentationScope;
20 :
21 : /// The resource associated with this log record.
22 : Resource? get resource;
23 :
24 : /// The count of attributes that were dropped due to limits.
25 : int get droppedAttributesCount;
26 :
27 : /// The trace ID associated with this log record, if any.
28 : TraceId? get traceId;
29 :
30 : /// The span ID associated with this log record, if any.
31 : SpanId? get spanId;
32 :
33 : /// The trace flags associated with this log record, if any.
34 : TraceFlags? get traceFlags;
35 : }
36 :
37 : /// A mutable view of a log record.
38 : ///
39 : /// This interface extends ReadableLogRecord to provide write access to
40 : /// log record fields. It is used by LogRecordProcessors that need to
41 : /// modify log records.
42 : ///
43 : /// Implementations are NOT required to be concurrent-safe. Asynchronous
44 : /// processors should clone the log record if needed.
45 : ///
46 : /// More information:
47 : /// https://opentelemetry.io/docs/specs/otel/logs/sdk/#readwritelogrecord
48 : abstract class ReadWriteLogRecord implements ReadableLogRecord {
49 : /// Sets the timestamp of when the event occurred.
50 : set timestamp(Int64? value);
51 :
52 : /// Sets the timestamp of when the event was observed.
53 : set observedTimestamp(Int64? value);
54 :
55 : /// Sets the severity number.
56 : set severityNumber(Severity? value);
57 :
58 : /// Sets the severity text.
59 : set severityText(String? value);
60 :
61 : /// Sets the body of the log record.
62 : set body(dynamic value);
63 :
64 : /// Sets the attributes of the log record.
65 : set attributes(Attributes? value);
66 :
67 : /// Sets the event name.
68 : set eventName(String? value);
69 :
70 : /// Sets the trace ID.
71 : set traceId(TraceId? value);
72 :
73 : /// Sets the span ID.
74 : set spanId(SpanId? value);
75 :
76 : /// Sets the trace flags.
77 : set traceFlags(TraceFlags? value);
78 :
79 : /// Adds an attribute to the log record.
80 : ///
81 : /// @param attribute The attribute to add
82 : void addAttribute(Attribute attribute);
83 :
84 : /// Removes an attribute from the log record by key.
85 : ///
86 : /// @param key The key of the attribute to remove
87 : void removeAttribute(String key);
88 :
89 : /// Creates a deep clone of this log record.
90 : ///
91 : /// This is useful for async processors that need to avoid race conditions.
92 : ReadWriteLogRecord clone();
93 : }
94 :
95 : /// SDK implementation of ReadWriteLogRecord.
96 : ///
97 : /// This class provides the concrete implementation of a log record with
98 : /// both read and write capabilities.
99 : class SDKLogRecord implements ReadWriteLogRecord {
100 : Int64? _timestamp;
101 : Int64? _observedTimestamp;
102 : final Context? _context;
103 : Severity? _severityNumber;
104 : String? _severityText;
105 : dynamic _body;
106 : Attributes? _attributes;
107 : String? _eventName;
108 : TraceId? _traceId;
109 : SpanId? _spanId;
110 : TraceFlags? _traceFlags;
111 : int _droppedAttributesCount = 0;
112 :
113 : final InstrumentationScope _instrumentationScope;
114 : Resource? _resource;
115 :
116 : /// Maximum number of attributes allowed per log record.
117 : /// This can be configured via LogRecordLimits.
118 : static int maxAttributeCount = 128;
119 :
120 : /// Maximum length of attribute values.
121 : /// This can be configured via LogRecordLimits.
122 : static int? maxAttributeValueLength;
123 :
124 : /// Creates a new SDK log record.
125 : ///
126 : /// @param instrumentationScope The instrumentation scope for this log record
127 : /// @param resource The resource associated with this log record
128 : /// @param timestamp When the event occurred
129 : /// @param observedTimestamp When the event was observed
130 : /// @param context The context associated with this log record
131 : /// @param severityNumber The severity level
132 : /// @param severityText The severity text
133 : /// @param body The log message body
134 : /// @param attributes Additional attributes
135 : /// @param eventName The event name
136 16 : SDKLogRecord({
137 : required InstrumentationScope instrumentationScope,
138 : Resource? resource,
139 : Int64? timestamp,
140 : Int64? observedTimestamp,
141 : Context? context,
142 : Severity? severityNumber,
143 : String? severityText,
144 : dynamic body,
145 : Attributes? attributes,
146 : String? eventName,
147 : }) : _instrumentationScope = instrumentationScope,
148 : _resource = resource,
149 : _timestamp = timestamp,
150 : _observedTimestamp = observedTimestamp,
151 : _context = context,
152 : _severityNumber = severityNumber,
153 : _severityText = severityText,
154 : _body = body,
155 : _attributes = attributes,
156 : _eventName = eventName {
157 : // Extract trace context from Context if available
158 16 : if (_context != null) {
159 18 : final spanContext = _context!.spanContext;
160 2 : if (spanContext != null && spanContext.isValid) {
161 4 : _traceId = spanContext.traceId;
162 4 : _spanId = spanContext.spanId;
163 4 : _traceFlags = spanContext.traceFlags;
164 : }
165 : }
166 :
167 : // Apply attribute limits
168 16 : _applyAttributeLimits();
169 : }
170 :
171 16 : void _applyAttributeLimits() {
172 16 : if (_attributes == null) return;
173 :
174 16 : final attrList = _attributes!.toList();
175 16 : if (attrList.length > maxAttributeCount) {
176 4 : final dropped = attrList.length - maxAttributeCount;
177 4 : _droppedAttributesCount += dropped;
178 2 : _attributes =
179 6 : OTel.attributesFromList(attrList.take(maxAttributeCount).toList());
180 :
181 2 : if (OTelLog.isDebug()) {
182 2 : OTelLog.debug(
183 2 : 'SDKLogRecord: Dropped $dropped attributes due to limit ($maxAttributeCount)');
184 : }
185 : }
186 : }
187 :
188 13 : @override
189 13 : Int64? get timestamp => _timestamp;
190 :
191 1 : @override
192 : set timestamp(Int64? value) {
193 1 : _timestamp = value;
194 : }
195 :
196 14 : @override
197 14 : Int64? get observedTimestamp => _observedTimestamp;
198 :
199 1 : @override
200 : set observedTimestamp(Int64? value) {
201 1 : _observedTimestamp = value;
202 : }
203 :
204 1 : @override
205 1 : Context? get context => _context;
206 :
207 14 : @override
208 14 : Severity? get severityNumber => _severityNumber;
209 :
210 1 : @override
211 : set severityNumber(Severity? value) {
212 1 : _severityNumber = value;
213 : }
214 :
215 14 : @override
216 14 : String? get severityText => _severityText;
217 :
218 1 : @override
219 : set severityText(String? value) {
220 1 : _severityText = value;
221 : }
222 :
223 15 : @override
224 15 : dynamic get body => _body;
225 :
226 1 : @override
227 : set body(dynamic value) {
228 1 : _body = value;
229 : }
230 :
231 14 : @override
232 14 : Attributes? get attributes => _attributes;
233 :
234 1 : @override
235 : set attributes(Attributes? value) {
236 1 : _attributes = value;
237 1 : _applyAttributeLimits();
238 : }
239 :
240 11 : @override
241 11 : String? get eventName => _eventName;
242 :
243 1 : @override
244 : set eventName(String? value) {
245 1 : _eventName = value;
246 : }
247 :
248 9 : @override
249 9 : InstrumentationScope get instrumentationScope => _instrumentationScope;
250 :
251 10 : @override
252 10 : Resource? get resource => _resource;
253 :
254 : /// Sets the resource for this log record.
255 1 : set resource(Resource? value) {
256 1 : _resource = value;
257 : }
258 :
259 8 : @override
260 8 : int get droppedAttributesCount => _droppedAttributesCount;
261 :
262 14 : @override
263 14 : TraceId? get traceId => _traceId;
264 :
265 4 : @override
266 : set traceId(TraceId? value) {
267 4 : _traceId = value;
268 : }
269 :
270 14 : @override
271 14 : SpanId? get spanId => _spanId;
272 :
273 4 : @override
274 : set spanId(SpanId? value) {
275 4 : _spanId = value;
276 : }
277 :
278 8 : @override
279 8 : TraceFlags? get traceFlags => _traceFlags;
280 :
281 3 : @override
282 : set traceFlags(TraceFlags? value) {
283 3 : _traceFlags = value;
284 : }
285 :
286 1 : @override
287 : void addAttribute(Attribute attribute) {
288 1 : if (_attributes == null) {
289 3 : _attributes = OTel.attributesFromList([attribute]);
290 : } else {
291 2 : final currentList = _attributes!.toList();
292 2 : if (currentList.length >= maxAttributeCount) {
293 2 : _droppedAttributesCount++;
294 1 : if (OTelLog.isDebug()) {
295 1 : OTelLog.debug(
296 2 : 'SDKLogRecord: Dropped attribute ${attribute.key} due to limit');
297 : }
298 : return;
299 : }
300 4 : _attributes = OTel.attributesFromList([...currentList, attribute]);
301 : }
302 : }
303 :
304 1 : @override
305 : void removeAttribute(String key) {
306 1 : if (_attributes == null) return;
307 7 : final filtered = _attributes!.toList().where((a) => a.key != key).toList();
308 2 : _attributes = OTel.attributesFromList(filtered);
309 : }
310 :
311 9 : @override
312 : ReadWriteLogRecord clone() {
313 9 : return SDKLogRecord(
314 9 : instrumentationScope: _instrumentationScope,
315 9 : resource: _resource,
316 9 : timestamp: _timestamp,
317 9 : observedTimestamp: _observedTimestamp,
318 9 : context: _context,
319 9 : severityNumber: _severityNumber,
320 9 : severityText: _severityText,
321 9 : body: _body,
322 9 : attributes: _attributes,
323 9 : eventName: _eventName,
324 : )
325 18 : .._traceId = _traceId
326 18 : .._spanId = _spanId
327 18 : .._traceFlags = _traceFlags
328 18 : .._droppedAttributesCount = _droppedAttributesCount;
329 : }
330 :
331 9 : @override
332 : String toString() {
333 9 : final buffer = StringBuffer('SDKLogRecord{');
334 27 : buffer.write('timestamp: $timestamp');
335 27 : buffer.write(', observedTimestamp: $observedTimestamp');
336 27 : buffer.write(', severity: $severityNumber');
337 27 : if (severityText != null) buffer.write(' ($severityText)');
338 36 : if (body != null) buffer.write(', body: $body');
339 12 : if (eventName != null) buffer.write(', eventName: $eventName');
340 12 : if (traceId != null) buffer.write(', traceId: $traceId');
341 12 : if (spanId != null) buffer.write(', spanId: $spanId');
342 15 : if (attributes != null && attributes!.length > 0) {
343 8 : buffer.write(', attributes: ${attributes!.length} items');
344 : }
345 9 : buffer.write('}');
346 9 : return buffer.toString();
347 : }
348 : }
|