timber_rust/config/entry.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
3
4#[cfg(feature = "loki")]
5use crate::config::duration::FlexibleDuration;
6#[cfg(feature = "aws")]
7use crate::config::timestamp::Timestamp;
8#[cfg(feature = "aws")]
9use crate::service::CloudWatchConfig;
10#[cfg(feature = "loki")]
11use crate::service::LokiConfig;
12#[cfg(feature = "loki")]
13use crate::BasicAuth;
14use crate::Concurrency;
15use serde::{Deserialize, Serialize};
16
17/// Represents the destination and configuration for a logging channel.
18///
19/// This enum defines where log entries are sent and how they are processed.
20/// It supports various outputs ranging from standard streams to cloud-based
21/// collectors like Grafana Loki.
22///
23/// See [`LogManager`][`crate::LogManager`] (represents a channel).
24#[derive(Clone, Debug, Serialize, Deserialize)]
25pub enum Entry {
26 /// A "black hole" destination.
27 /// All logs sent to this channel are silently discarded.
28 ///
29 /// See: [`SilentLogger`][`crate::SilentLogger`].
30 Silent {},
31
32 /// Standard Output (stdout).
33 /// Logs are printed directly to the terminal's standard output stream.
34 ///
35 /// - See: [`service::write::Cout`][`crate::service::write::Cout`]
36 /// - See: [`logger::Direct`][`crate::logger::Direct`]
37 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
38 StdOut {
39 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
40 /// or [`Concurrency::Async`] for background-threaded logging.
41 concurrency: Concurrency,
42 /// Optional override for the maximum number of write retries.
43 /// If `None`, the factory default (usually 3) is used.
44 max_retries: Option<usize>,
45 /// Optional override for the number of background worker threads.
46 /// If `None`, the factory default (usually 1) is used.
47 /// **Note:** A value of 1 is recommended to prevent interlaced output.
48 worker_count: Option<usize>,
49 },
50
51 /// Standard Error (stderr).
52 /// Logs are printed to the terminal's standard error stream, typically
53 /// used for diagnostics or errors.
54 ///
55 /// - See: [`service::write::Cerr`][`crate::service::write::Cerr`].
56 /// - See: [`logger::Direct`][`crate::logger::Direct`]
57 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
58 StdErr {
59 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
60 /// or [`Concurrency::Async`] for background-threaded logging.
61 concurrency: Concurrency,
62 /// Optional override for the maximum number of write retries.
63 /// If `None`, the factory default (usually 3) is used.
64 max_retries: Option<usize>,
65 /// Optional override for the number of background worker threads.
66 /// If `None`, the factory default (usually 1) is used.
67 /// **Note:** A value of 1 is recommended to prevent interlaced output.
68 worker_count: Option<usize>,
69 },
70
71 /// Unbuffered File Output.
72 /// Logs are written directly to a file on disk. Each write is typically
73 /// immediate, ensuring data integrity at the cost of higher I/O overhead.
74 ///
75 /// - See: [`service::write::File`][`crate::service::FileWrite`].
76 /// - See: [`logger::Direct`][`crate::logger::Direct`]
77 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
78 File {
79 /// Path to the file where to dump the log.
80 path: String,
81 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
82 /// or [`Concurrency::Async`] for background-threaded logging.
83 concurrency: Concurrency,
84 /// Optional override for the maximum number of write retries.
85 /// If `None`, the factory default (usually 3) is used.
86 max_retries: Option<usize>,
87 /// Optional override for the number of background worker threads.
88 /// If `None`, the factory default (usually 1) is used.
89 /// **Note:** A value of 1 is recommended to prevent interlaced output.
90 worker_count: Option<usize>,
91 },
92
93 /// Performance-Optimized Buffered File Output.
94 /// Logs are accumulated in a memory buffer before being flushed to disk.
95 ///
96 /// ### ⚠️ Warning
97 /// Use with caution. Because logs are held in memory, a sudden application
98 /// crash or panic may result in the loss of the most recent log entries.
99 ///
100 /// Unbuffered File Output.
101 /// Logs are written directly to a file on disk. Each write is typically
102 /// immediate, ensuring data integrity at the cost of higher I/O overhead.
103 ///
104 /// - See: [`service::write::BufferedFile`][`crate::service::write::BufferedFile`].
105 /// - See: [`service::IoWrite`][`crate::service::IoWrite`].
106 /// - See: [`std::fs::File`]
107 /// - See: [`std::io::BufWriter`]
108 /// - See: [`logger::Direct`][`crate::logger::Direct`]
109 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
110 BufferedFile {
111 /// Path to the file where to dump the log.
112 path: String,
113 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
114 /// or [`Concurrency::Async`] for background-threaded logging.
115 concurrency: Concurrency,
116 /// Optional override for the maximum number of write retries.
117 /// If `None`, the factory default (usually 3) is used.
118 max_retries: Option<usize>,
119 /// Optional override for the number of background worker threads.
120 /// If `None`, the factory default (usually 1) is used.
121 /// **Note:** A value of 1 is recommended to prevent interlaced output.
122 worker_count: Option<usize>,
123 },
124
125 /// Memory Buffer.
126 /// Captures logs into an internal string buffer, useful for testing
127 /// or displaying logs within an application UI.
128 ///
129 /// - See: [`service::write::StringFmt`][`crate::service::write::StringFmt`].
130 /// - See: [`logger::Direct`][`crate::logger::Direct`]
131 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
132 String {
133 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
134 /// or [`Concurrency::Async`] for background-threaded logging.
135 concurrency: Concurrency,
136 /// Optional override for the maximum number of write retries.
137 /// If `None`, the factory default (usually 3) is used.
138 max_retries: Option<usize>,
139 /// Optional override for the number of background worker threads.
140 /// If `None`, the factory default (usually 1) is used.
141 /// **Note:** A value of 1 is recommended to prevent interlaced output.
142 worker_count: Option<usize>,
143 /// String capacity
144 capacity: Option<usize>,
145 },
146
147 /// Memory Buffer (vector).
148 /// Captures logs into an internal vector, useful for testing.
149 ///
150 /// - See: [`service::Vector`][`crate::service::Vector`].
151 /// - See: [`logger::Direct`][`crate::logger::Direct`]
152 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
153 Vector {
154 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
155 /// or [`Concurrency::Async`] for background-threaded logging.
156 concurrency: Concurrency,
157 /// Optional override for the maximum number of write retries.
158 /// If `None`, the factory default (usually 3) is used.
159 max_retries: Option<usize>,
160 /// Optional override for the number of background worker threads.
161 /// If `None`, the factory default (usually 1) is used.
162 /// **Note:** A value of 1 is recommended to prevent interlaced output.
163 worker_count: Option<usize>,
164 /// Vector capacity
165 capacity: Option<usize>,
166 },
167
168 /// Grafana Loki Integration.
169 /// Pushes logs to a remote Loki instance via HTTP/HTTPS.
170 ///
171 /// This variant is only available when the `loki` feature is enabled.
172 ///
173 /// - See: [`LokiLogger`][`crate::logger::Loki`].
174 /// - See: [`LokiService`][`crate::service::Loki`].
175 /// - See: [`LokiConfig`][`crate::service::LokiConfig`].
176 /// - See: [`logger::Direct`][`crate::logger::Direct`]
177 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
178 #[cfg(feature = "loki")]
179 #[cfg_attr(docsrs, doc(cfg(feature = "loki")))]
180 Loki {
181 /// The full HTTP/HTTPS endpoint for the Loki push API
182 /// (e.g., `https://logs-prod-us-central1.grafana.net/loki/api/v1/push`).
183 url: String,
184 /// The name of the application. This becomes a static label used for
185 /// filtering logs in Grafana.
186 app: String,
187 /// The job name associated with the process. Useful for distinguishing
188 /// between different instances of the same application.
189 job: String,
190 /// The deployment environment (e.g., "production", "staging", "development").
191 /// Helps isolate logs across different stages of the lifecycle.
192 env: String,
193 /// Optional credentials for Basic Authentication.
194 basic_auth: Option<BasicAuth>,
195 /// Optional Bearer token for API authentication (e.g., for Grafana Cloud).
196 bearer_auth: Option<String>,
197 /// The maximum time allowed to establish a connection to the Loki server.
198 connection_timeout: FlexibleDuration,
199 /// The maximum time allowed for a single push request to complete.
200 request_timeout: FlexibleDuration,
201 /// How many times a failed push should be retried before falling back.
202 /// This is handled by the internal dispatch logic.
203 max_retries: usize,
204 /// The number of background worker threads dedicated to pushing logs.
205 /// Higher counts increase throughput but consume more network resources.
206 worker_count: usize,
207 },
208
209 /// AWS Cloudwatch Integration.
210 /// Pushes logs to a remote Cloudwatch Integration instance via AWS SDK.
211 ///
212 /// This variant is only available when the `aws` feature is enabled.
213 ///
214 /// - See: [`CloudwatchLogger`][`crate::logger::CloudWatch`].
215 /// - See: [`CloudwatchService`][`crate::service::CloudWatch`].
216 /// - See: [`CloudwatchConfig`][`crate::service::LokiConfig`].
217 /// - See: [`logger::Direct`][`crate::logger::Direct`]
218 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
219 #[cfg(feature = "aws")]
220 #[cfg_attr(docsrs, doc(cfg(feature = "aws")))]
221 CloudWatchEnv {
222 /// Log group for the cloudwatch service. The config is loaded from ENV.
223 log_group: String,
224 },
225
226 /// AWS Cloudwatch Integration.
227 /// Pushes logs to a remote Cloudwatch Integration instance via AWS SDK.
228 ///
229 /// This variant is only available when the `aws` feature is enabled.
230 ///
231 /// - See: [`CloudwatchLogger`][`crate::logger::CloudWatch`].
232 /// - See: [`CloudwatchService`][`crate::service::CloudWatch`].
233 /// - See: [`CloudwatchConfig`][`crate::service::LokiConfig`].
234 /// - See: [`logger::Direct`][`crate::logger::Direct`]
235 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
236 #[cfg(feature = "aws")]
237 #[cfg_attr(docsrs, doc(cfg(feature = "aws")))]
238 CloudWatchConfig {
239 /// AWS Access Key ID used for authentication.
240 access_key_id: String,
241 /// AWS Secret Access Key. This field is sensitive and hidden in Debug logs.
242 access_key_secret: String,
243 /// Optional session token for temporary credentials (STS).
244 session_token: Option<String>,
245 /// Optional expiration timestamp for the credentials in seconds.
246 expires_in: Option<Timestamp>,
247 /// The name of the CloudWatch Log Group where logs will be sent.
248 log_group: String,
249 /// The AWS Region (e.g., "us-east-1").
250 region: String,
251 },
252
253 /// AWS CloudWatch (via Standard Output).
254 ///
255 /// Formats logs as single-line JSON objects and prints them to `stdout`.
256 /// This is the preferred method for AWS Lambda, ECS (with `awslogs` driver),
257 /// and Fargate, as it avoids the overhead of the AWS SDK while maintaining
258 /// structured logs.
259 ///
260 /// - See: [`CloudWatchCout`][`crate::service::CloudWatchCout`].
261 /// - See: [`CloudWatchCoutMessageFormatter`][`crate::service::CloudWatchCoutMessageFormatter`].
262 /// - See: [`logger::Direct`][`crate::logger::Direct`]
263 /// - See: [`logger::Queued`][`crate::`logger::Queued`]
264 #[cfg(feature = "awscout")]
265 #[cfg_attr(docsrs, doc(cfg(feature = "awscout")))]
266 CloudWatchCout {
267 /// The execution strategy: [`Concurrency::Sync`] for immediate writes,
268 /// or [`Concurrency::Async`] for background-threaded logging.
269 concurrency: Concurrency,
270 /// Optional override for the maximum number of write retries.
271 /// If `None`, the factory default (usually 3) is used.
272 max_retries: Option<usize>,
273 /// Optional override for the number of background worker threads.
274 /// If `None`, the factory default (usually 1) is used.
275 /// **Note:** A value of 1 is recommended to prevent interlaced output.
276 worker_count: Option<usize>,
277 },
278
279 /// Placeholder for missing functionality.
280 /// Used when a configuration specifies a model (like `loki`) but the
281 /// required crate feature was not enabled at compile time.
282 DisabledFeature {
283 /// The name of the feature that is currently missing.
284 feature: String,
285 },
286}
287
288impl Entry {
289 /// Creates a configuration for a silent logger that discards all input.
290 ///
291 /// Useful as a placeholder or for completely disabling output in specific environments.
292 pub fn silent() -> Self {
293 Entry::Silent {}
294 }
295
296 /// Configures logging to the Standard Output stream (stdout).
297 ///
298 /// This is the primary target for CLI applications and containerized services.
299 pub fn stdout(concurrency: Concurrency) -> Self {
300 Entry::StdOut {
301 concurrency,
302 max_retries: None,
303 worker_count: None,
304 }
305 }
306
307 /// Configures logging to the Standard Error stream (stderr).
308 ///
309 /// Recommended for diagnostic messages to keep the main stdout stream clean for data.
310 pub fn stderr(concurrency: Concurrency) -> Self {
311 Entry::StdErr {
312 concurrency,
313 max_retries: None,
314 worker_count: None,
315 }
316 }
317
318 /// Configures an in-memory string buffer for captured logs.
319 ///
320 /// Useful for small-scale log capturing where a full Vector of messages is not required.
321 pub fn string(concurrency: Concurrency) -> Self {
322 Entry::String {
323 concurrency,
324 max_retries: None,
325 worker_count: None,
326 capacity: None,
327 }
328 }
329
330 /// Configures a raw file logger at the specified path.
331 ///
332 /// Opens the file in append mode. Ensure the process has appropriate write permissions.
333 pub fn file(concurrency: Concurrency, path: String) -> Self {
334 Entry::File {
335 path,
336 concurrency,
337 max_retries: None,
338 worker_count: None,
339 }
340 }
341
342 /// Configures a buffered file logger at the specified path.
343 ///
344 /// Wraps the file in a buffer to reduce the frequency of underlying system calls,
345 /// significantly improving performance during high-volume bursts.
346 pub fn buffered_file(concurrency: Concurrency, path: String) -> Self {
347 Entry::BufferedFile {
348 path,
349 concurrency,
350 max_retries: None,
351 worker_count: None,
352 }
353 }
354
355 /// Configures a logger that captures structured `Message` objects in a `Vec`.
356 ///
357 /// This is the "gold standard" for unit testing, allowing for precise assertions
358 /// on log levels and contents.
359 pub fn vector(concurrency: Concurrency) -> Self {
360 Entry::Vector {
361 concurrency,
362 max_retries: None,
363 worker_count: None,
364 capacity: None,
365 }
366 }
367
368 /// Integrates a Grafana Loki configuration into the entry list.
369 ///
370 /// This converts a high-level [`LokiConfig`] into the flat enum representation.
371 #[cfg(feature = "loki")]
372 #[cfg_attr(docsrs, doc(cfg(feature = "loki")))]
373 pub fn loki(config: LokiConfig) -> Self {
374 Entry::Loki {
375 url: config.url,
376 app: config.app,
377 job: config.job,
378 env: config.env,
379 basic_auth: config.basic_auth,
380 bearer_auth: config.bearer_auth,
381 connection_timeout: config.connection_timeout.into(),
382 request_timeout: config.request_timeout.into(),
383 max_retries: config.max_retries,
384 worker_count: config.worker_count,
385 }
386 }
387
388 /// Configures Amazon CloudWatch using explicit credentials.
389 ///
390 /// Use this when credentials are provided manually or via a secret manager.
391 #[cfg(feature = "aws")]
392 #[cfg_attr(docsrs, doc(cfg(feature = "aws")))]
393 pub fn cloudwatch_config(config: CloudWatchConfig) -> Self {
394 Entry::CloudWatchConfig {
395 access_key_id: config.access_key_id,
396 access_key_secret: config.access_key_secret,
397 session_token: config.session_token,
398 expires_in: config.expires_in.map(|t| Timestamp::from(t)),
399 log_group: config.log_group,
400 region: config.region,
401 }
402 }
403
404 /// Configures Amazon CloudWatch using credentials sourced from the environment.
405 ///
406 /// Standard AWS environment variables (AWS_ACCESS_KEY_ID, etc.) will be used automatically.
407 #[cfg(feature = "aws")]
408 #[cfg_attr(docsrs, doc(cfg(feature = "aws")))]
409 pub fn cloudwatch_env(log_group: String) -> Self {
410 Entry::CloudWatchEnv { log_group }
411 }
412
413 /// Configures a CloudWatch-formatted logger that writes to stdout.
414 ///
415 /// This allows logs to be formatted for AWS CloudWatch even if they are
416 /// being emitted to a local terminal or captured by a separate log agent.
417 #[cfg(feature = "aws")]
418 #[cfg_attr(docsrs, doc(cfg(feature = "aws")))]
419 pub fn cloudwatch_cout(concurrency: Concurrency) -> Self {
420 Entry::CloudWatchCout {
421 concurrency,
422 worker_count: None,
423 max_retries: None,
424 }
425 }
426}