Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
5 :
6 : import '../../environment/otel_env.dart';
7 : import '../../otel.dart';
8 : import '../../resource/resource.dart';
9 : import '../log_record_processor.dart';
10 : import '../logger_provider.dart';
11 : import 'batch_log_record_processor.dart';
12 : import 'console_log_record_exporter.dart';
13 : import 'log_record_exporter.dart';
14 : import 'otlp/http/otlp_http_log_record_exporter.dart';
15 : import 'otlp/http/otlp_http_log_record_exporter_config.dart';
16 : import 'otlp/otlp_grpc_log_record_exporter.dart';
17 : import 'otlp/otlp_grpc_log_record_exporter_config.dart';
18 : import 'simple_log_record_processor.dart';
19 :
20 : /// Configuration for logs exporters and processors.
21 : ///
22 : /// This class provides methods to configure the LoggerProvider based on
23 : /// environment variables and explicit configuration parameters.
24 : class LogsConfiguration {
25 : /// Configures a LoggerProvider with the given settings.
26 : ///
27 : /// This configures everything needed for the logs pipeline:
28 : /// - An exporter (based on OTEL_LOGS_EXPORTER env var or defaults to OTLP)
29 : /// - A processor (BatchLogRecordProcessor with BLRP env var config)
30 : /// - Sets up resources on the LoggerProvider
31 : ///
32 : /// @param endpoint The endpoint URL for the exporter (null to use the
33 : /// protocol-dependent default, issue #220)
34 : /// @param secure Whether to use TLS for gRPC connections
35 : /// @param logRecordExporter Optional custom exporter (overrides env var)
36 : /// @param logRecordProcessor Optional custom processor (overrides env var)
37 : /// @param resource Optional resource for the LoggerProvider
38 : /// @return The configured LoggerProvider
39 127 : static LoggerProvider configureLoggerProvider({
40 : String? endpoint,
41 : bool? secure,
42 : LogRecordExporter? logRecordExporter,
43 : LogRecordProcessor? logRecordProcessor,
44 : Resource? resource,
45 : OtlpEnvironmentValues? otlpConfig,
46 : List<String>? exporters,
47 : BlrpEnvironmentValues? blrpConfig,
48 : }) {
49 4 : otlpConfig ??= OTelEnv.getOtlpConfig(signal: 'logs');
50 8 : exporters ??= OTelEnv.getExporters(signal: 'logs') ?? ['otlp'];
51 4 : blrpConfig ??= OTelEnv.getBlrpConfig();
52 :
53 : // Get the logger provider
54 127 : final logProvider = OTel.loggerProvider();
55 :
56 : // Set resource if provided
57 : if (resource != null) {
58 127 : logProvider.resource = resource;
59 : }
60 :
61 : // If a custom processor is provided, use it directly
62 : if (logRecordProcessor != null) {
63 3 : logProvider.addLogRecordProcessor(logRecordProcessor);
64 : return logProvider;
65 : }
66 :
67 : // Explicitly provided exporter wins; otherwise read the env selection.
68 : if (logRecordExporter != null) {
69 4 : logProvider.addLogRecordProcessor(
70 4 : _createProcessor(logRecordExporter, blrpConfig));
71 : return logProvider;
72 : }
73 :
74 : // Multiple exporters install one processor per exporter.
75 124 : if (exporters.contains('none')) {
76 5 : if (exporters.length > 1 && OTelLog.isWarn()) {
77 1 : OTelLog.warn("OTEL_LOGS_EXPORTER contains 'none' alongside other "
78 : 'values; installing no processor.');
79 2 : } else if (OTelLog.isDebug()) {
80 1 : OTelLog.debug(
81 : 'LogsConfiguration: OTEL_LOGS_EXPORTER=none, no processor added');
82 : }
83 : return logProvider;
84 : }
85 :
86 123 : final createdExporters = <LogRecordExporter>[];
87 246 : for (final name in exporters) {
88 123 : if (name == 'logging') {
89 1 : if (OTelLog.isWarn()) {
90 1 : OTelLog.warn("OTEL_LOGS_EXPORTER value 'logging' is deprecated "
91 : "in the spec and not supported; use 'console'.");
92 : }
93 : continue;
94 : }
95 123 : final created = _createExporter(name, endpoint, secure, otlpConfig);
96 : if (created != null) {
97 123 : createdExporters.add(created);
98 1 : } else if (OTelLog.isWarn()) {
99 2 : OTelLog.warn("OTEL_LOGS_EXPORTER value '$name' is not supported; "
100 : 'ignoring. Supported: otlp, console, none.');
101 : }
102 : }
103 123 : if (createdExporters.isEmpty) {
104 1 : if (OTelLog.isWarn()) {
105 1 : OTelLog.warn('OTEL_LOGS_EXPORTER produced no usable exporter; '
106 : 'falling back to the default otlp exporter.');
107 : }
108 1 : final fallback = _createExporter('otlp', endpoint, secure, otlpConfig);
109 : if (fallback != null) {
110 1 : createdExporters.add(fallback);
111 : }
112 : }
113 246 : for (final exporter in createdExporters) {
114 246 : logProvider.addLogRecordProcessor(_createProcessor(exporter, blrpConfig));
115 : }
116 :
117 123 : if (OTelLog.isDebug()) {
118 242 : OTelLog.debug('LogsConfiguration: Configured LoggerProvider with '
119 121 : '${createdExporters.length} exporter(s) from OTEL_LOGS_EXPORTER');
120 : }
121 :
122 : return logProvider;
123 : }
124 :
125 : /// Creates a log record exporter based on the exporter type.
126 123 : static LogRecordExporter? _createExporter(
127 : String exporterType,
128 : String? endpoint,
129 : bool? secure,
130 : OtlpEnvironmentValues otlpConfig,
131 : ) {
132 : final protocol = otlpConfig.protocol ?? 'http/protobuf';
133 :
134 : // Use env endpoint if available, otherwise use provided endpoint. The
135 : // default endpoint depends on the protocol (issue #220): OTLP/gRPC uses
136 : // port 4317, the HTTP protocols use port 4318.
137 : final effectiveEndpoint = otlpConfig.endpoint ??
138 : endpoint ??
139 97 : (protocol == 'grpc' ? OTel.defaultGrpcEndpoint : OTel.defaultEndpoint);
140 : final envInsecure = otlpConfig.insecure;
141 123 : final effectiveSecure = OTelEnv.resolveOtlpSecure(
142 : envInsecure: envInsecure,
143 : endpoint: effectiveEndpoint,
144 : explicitSecure: secure,
145 : );
146 :
147 123 : if (exporterType == 'console') {
148 1 : if (OTelLog.isDebug()) {
149 0 : OTelLog.debug('LogsConfiguration: Creating ConsoleLogRecordExporter');
150 : }
151 1 : return ConsoleLogRecordExporter();
152 : }
153 :
154 123 : if (exporterType == 'otlp') {
155 123 : if (protocol == 'grpc') {
156 3 : if (OTelLog.isDebug()) {
157 2 : OTelLog.debug(
158 : 'LogsConfiguration: Creating OtlpGrpcLogRecordExporter');
159 : }
160 3 : return OtlpGrpcLogRecordExporter(
161 3 : OtlpGrpcLogRecordExporterConfig(
162 : endpoint: effectiveEndpoint,
163 : insecure: !effectiveSecure,
164 3 : headers: otlpConfig.headers ?? {},
165 : timeout: otlpConfig.timeout ?? const Duration(seconds: 10),
166 3 : compression: otlpConfig.compression == 'gzip',
167 : certificate: otlpConfig.certificate,
168 : clientKey: otlpConfig.clientKey,
169 : clientCertificate: otlpConfig.clientCertificate,
170 : ),
171 : );
172 : } else {
173 : // Default to http/protobuf
174 122 : if (OTelLog.isDebug()) {
175 120 : OTelLog.debug(
176 : 'LogsConfiguration: Creating OtlpHttpLogRecordExporter');
177 : }
178 122 : return OtlpHttpLogRecordExporter(
179 122 : OtlpHttpLogRecordExporterConfig(
180 : endpoint: effectiveEndpoint,
181 122 : headers: otlpConfig.headers ?? {},
182 : timeout: otlpConfig.timeout ?? const Duration(seconds: 10),
183 122 : compression: otlpConfig.compression == 'gzip',
184 : certificate: otlpConfig.certificate,
185 : clientKey: otlpConfig.clientKey,
186 : clientCertificate: otlpConfig.clientCertificate,
187 : ),
188 : );
189 : }
190 : }
191 :
192 : // Unknown exporter type
193 1 : if (OTelLog.isDebug()) {
194 0 : OTelLog.debug('LogsConfiguration: Unknown exporter type: $exporterType');
195 : }
196 : return null;
197 : }
198 :
199 : /// Creates a log record processor with BLRP configuration from environment.
200 125 : static LogRecordProcessor _createProcessor(
201 : LogRecordExporter exporter, BlrpEnvironmentValues blrpConfig) {
202 : final processorConfig =
203 125 : BatchLogRecordProcessorConfig.fromBlrpEnvironmentValues(blrpConfig);
204 125 : return BatchLogRecordProcessor(exporter, processorConfig);
205 : }
206 :
207 : /// Creates a simple (synchronous) log record processor instead of batch.
208 : ///
209 : /// This is useful for development/debugging or when you want immediate export.
210 2 : static LogRecordProcessor createSimpleProcessor(LogRecordExporter exporter) {
211 2 : return SimpleLogRecordProcessor(exporter);
212 : }
213 : }
|