Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : library;
5 :
6 : import 'package:meta/meta.dart';
7 : import '../../dartastic_opentelemetry.dart';
8 : import 'logger.dart';
9 :
10 : part 'logger_provider_create.dart';
11 :
12 : /// SDK implementation of the APILoggerProvider interface.
13 : ///
14 : /// The LoggerProvider is the entry point to the logging API. It is responsible
15 : /// for creating and managing Loggers, as well as configuring the logging
16 : /// pipeline via LogRecordProcessors and Exporters.
17 : ///
18 : /// This implementation delegates some functionality to the API LoggerProvider
19 : /// implementation while adding SDK-specific behaviors like log processor management
20 : /// and resource association.
21 : ///
22 : /// More information:
23 : /// https://opentelemetry.io/docs/specs/otel/logs/sdk/
24 : class LoggerProvider implements APILoggerProvider {
25 : /// Registry of loggers managed by this provider.
26 : final Map<String, OTelLogger> _loggers = {};
27 :
28 : /// Log record processors registered with this provider.
29 : final List<LogRecordProcessor> _logRecordProcessors = [];
30 :
31 : /// The underlying API LoggerProvider implementation.
32 : final APILoggerProvider _delegate;
33 :
34 : /// The resource associated with this provider.
35 : Resource? resource;
36 :
37 120 : @override
38 240 : bool get isShutdown => _delegate.isShutdown;
39 :
40 118 : @override
41 : set isShutdown(bool value) {
42 236 : _delegate.isShutdown = value;
43 : }
44 :
45 : /// Private constructor for creating LoggerProvider instances.
46 : ///
47 : /// @param delegate The API LoggerProvider implementation to delegate to
48 : /// @param resource Optional Resource describing the entity producing telemetry
49 120 : LoggerProvider._({
50 : required APILoggerProvider delegate,
51 : this.resource,
52 : }) : _delegate = delegate {
53 120 : if (OTelLog.isDebug()) {
54 360 : OTelLog.debug('LoggerProvider: Created with resource: $resource');
55 120 : if (resource != null) {
56 0 : OTelLog.debug('Resource attributes:');
57 0 : resource!.attributes.toList().forEach((attr) {
58 0 : OTelLog.debug(' ${attr.key}: ${attr.value}');
59 : });
60 : }
61 : }
62 : }
63 :
64 118 : @override
65 : Future<bool> shutdown() async {
66 118 : if (OTelLog.isDebug()) {
67 117 : OTelLog.debug(
68 351 : 'LoggerProvider: Shutting down with ${_logRecordProcessors.length} processors');
69 : }
70 :
71 118 : if (!isShutdown) {
72 : // Shutdown all log record processors
73 236 : for (final processor in _logRecordProcessors) {
74 118 : if (OTelLog.isDebug()) {
75 117 : OTelLog.debug(
76 234 : 'LoggerProvider: Shutting down processor ${processor.runtimeType}');
77 : }
78 : try {
79 118 : await processor.shutdown();
80 118 : if (OTelLog.isDebug()) {
81 117 : OTelLog.debug(
82 234 : 'LoggerProvider: Successfully shut down processor ${processor.runtimeType}');
83 : }
84 : } catch (e) {
85 1 : if (OTelLog.isDebug()) {
86 1 : OTelLog.debug(
87 2 : 'LoggerProvider: Error shutting down processor ${processor.runtimeType}: $e');
88 : }
89 : }
90 : }
91 :
92 : // Clear cached loggers
93 236 : _loggers.clear();
94 118 : if (OTelLog.isDebug()) {
95 117 : OTelLog.debug('LoggerProvider: Cleared cached loggers');
96 : }
97 :
98 : try {
99 236 : await _delegate.shutdown();
100 118 : if (OTelLog.isDebug()) {
101 117 : OTelLog.debug('LoggerProvider: Delegate shutdown complete');
102 : }
103 : } catch (e) {
104 0 : if (OTelLog.isDebug()) {
105 0 : OTelLog.debug('LoggerProvider: Error during delegate shutdown: $e');
106 : }
107 : }
108 :
109 118 : isShutdown = true;
110 235 : if (OTelLog.isDebug()) OTelLog.debug('LoggerProvider: Shutdown complete');
111 : } else {
112 131 : if (OTelLog.isDebug()) OTelLog.debug('LoggerProvider: Already shut down');
113 : }
114 118 : return isShutdown;
115 : }
116 :
117 9 : @override
118 : OTelLogger getLogger(
119 : String name, {
120 : String? version,
121 : String? schemaUrl,
122 : Attributes? attributes,
123 : }) {
124 9 : if (OTelLog.isDebug()) {
125 9 : OTelLog.debug(
126 9 : 'LoggerProvider: Getting logger with name: $name, version: $version, schemaUrl: $schemaUrl');
127 : }
128 9 : if (isShutdown) {
129 2 : throw StateError('LoggerProvider has been shut down');
130 : }
131 :
132 : // Ensure resource is set before creating logger
133 9 : ensureResourceIsSet();
134 :
135 9 : final key = '$name:${version ?? ''}';
136 18 : return _loggers.putIfAbsent(
137 : key,
138 18 : () => SDKLoggerCreate.create(
139 18 : delegate: _delegate.getLogger(
140 : name,
141 : version: version,
142 : schemaUrl: schemaUrl,
143 : attributes: attributes,
144 : ),
145 : provider: this,
146 : ),
147 : );
148 : }
149 :
150 : /// Adds a log record processor to this provider.
151 : ///
152 : /// Log record processors are notified when log records are emitted and are
153 : /// responsible for additional processing of logs, such as exporting them.
154 : ///
155 : /// @param processor The log record processor to add
156 120 : void addLogRecordProcessor(LogRecordProcessor processor) {
157 120 : if (isShutdown) {
158 2 : throw StateError('LoggerProvider has been shut down');
159 : }
160 120 : if (OTelLog.isDebug()) {
161 120 : OTelLog.debug(
162 240 : 'LoggerProvider: Adding log record processor of type ${processor.runtimeType}');
163 : }
164 240 : _logRecordProcessors.add(processor);
165 : }
166 :
167 : /// Gets all registered log record processors.
168 : ///
169 : /// @return An unmodifiable list of all log record processors
170 9 : List<LogRecordProcessor> get logRecordProcessors =>
171 18 : List.unmodifiable(_logRecordProcessors);
172 :
173 : /// Ensures the resource for this provider is properly set.
174 : ///
175 : /// If no resource has been set, the default resource will be used.
176 9 : void ensureResourceIsSet() {
177 9 : if (resource == null) {
178 1 : resource = OTel.defaultResource;
179 1 : if (OTelLog.isDebug()) {
180 1 : OTelLog.debug('LoggerProvider: Setting resource from default');
181 1 : if (resource != null) {
182 1 : OTelLog.debug('Resource attributes:');
183 5 : resource!.attributes.toList().forEach((attr) {
184 4 : if (attr.key == 'tenant_id' || attr.key == 'service.name') {
185 4 : OTelLog.debug(' ${attr.key}: ${attr.value}');
186 : }
187 : });
188 : }
189 : }
190 : }
191 : }
192 :
193 2 : @override
194 4 : String get endpoint => _delegate.endpoint;
195 :
196 2 : @override
197 : set endpoint(String value) {
198 4 : _delegate.endpoint = value;
199 : }
200 :
201 2 : @override
202 4 : String get serviceName => _delegate.serviceName;
203 :
204 2 : @override
205 : set serviceName(String value) {
206 4 : _delegate.serviceName = value;
207 : }
208 :
209 2 : @override
210 4 : String? get serviceVersion => _delegate.serviceVersion;
211 :
212 2 : @override
213 : set serviceVersion(String? value) {
214 4 : _delegate.serviceVersion = value;
215 : }
216 :
217 9 : @override
218 18 : bool get enabled => _delegate.enabled;
219 :
220 2 : @override
221 : set enabled(bool value) {
222 4 : _delegate.enabled = value;
223 : }
224 :
225 : /// Forces all log record processors to flush any queued log records.
226 : ///
227 : /// This method is useful for ensuring that all log records are exported
228 : /// before the application terminates or when immediate visibility
229 : /// of logs is required.
230 : ///
231 : /// @return A Future that completes when all processors have been flushed
232 4 : Future<void> forceFlush() async {
233 4 : if (OTelLog.isDebug()) {
234 4 : OTelLog.debug(
235 12 : 'LoggerProvider: Force flushing ${_logRecordProcessors.length} processors');
236 : }
237 :
238 4 : if (isShutdown) {
239 1 : if (OTelLog.isDebug()) {
240 1 : OTelLog.debug(
241 : 'LoggerProvider: Cannot force flush - provider is shut down');
242 : }
243 : return;
244 : }
245 :
246 8 : for (var processor in _logRecordProcessors) {
247 : try {
248 4 : if (OTelLog.isDebug()) {
249 4 : OTelLog.debug(
250 8 : 'LoggerProvider: Flushing processor ${processor.runtimeType}');
251 : }
252 4 : await processor.forceFlush();
253 3 : if (OTelLog.isDebug()) {
254 3 : OTelLog.debug(
255 6 : 'LoggerProvider: Successfully flushed processor ${processor.runtimeType}');
256 : }
257 : } catch (e) {
258 1 : if (OTelLog.isDebug()) {
259 1 : OTelLog.debug(
260 2 : 'LoggerProvider: Error flushing processor ${processor.runtimeType}: $e');
261 : }
262 : }
263 : }
264 :
265 4 : if (OTelLog.isDebug()) {
266 4 : OTelLog.debug('LoggerProvider: Force flush complete');
267 : }
268 : }
269 : }
|