witchcraft_server/logging/
mod.rs

1// Copyright 2021 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Logging APIs
16use crate::extensions::AuditLogEntry;
17use crate::logging::api::objects::{AuditLogV3, EventLogV2, RequestLogV2};
18use crate::shutdown_hooks::ShutdownHooks;
19use conjure_error::Error;
20use conjure_serde::json;
21use futures::executor::block_on;
22use futures::SinkExt;
23use lazycell::AtomicLazyCell;
24pub(crate) use logger::Appender;
25use once_cell::sync::OnceCell;
26use refreshable::Refreshable;
27use std::io;
28use std::io::Write as _;
29use std::sync::Arc;
30use tokio::sync::Mutex;
31use witchcraft_metrics::MetricRegistry;
32use witchcraft_server_config::install::InstallConfig;
33use witchcraft_server_config::runtime::LoggingConfig;
34
35pub use witchcraft_logging_api as api;
36
37mod cleanup;
38mod format;
39mod logger;
40pub mod mdc;
41mod metric;
42mod service;
43mod trace;
44
45pub(crate) static AUDIT_LOGGER: AtomicLazyCell<Arc<Mutex<Appender<AuditLogV3>>>> =
46    AtomicLazyCell::NONE;
47
48static EVENT_LOGGER: OnceCell<Appender<EventLogV2>> = OnceCell::new();
49
50pub(crate) const REQUEST_ID_KEY: &str = "_requestId";
51pub(crate) const SAMPLED_KEY: &str = "_sampled";
52
53pub(crate) struct Loggers {
54    pub request_logger: Arc<Appender<RequestLogV2>>,
55    pub audit_logger: Arc<Mutex<Appender<AuditLogV3>>>,
56}
57
58pub(crate) fn early_init() {
59    service::early_init()
60}
61
62pub(crate) async fn init(
63    metrics: &Arc<MetricRegistry>,
64    install: &InstallConfig,
65    runtime: &Refreshable<LoggingConfig, Error>,
66    hooks: &mut ShutdownHooks,
67) -> Result<Loggers, Error> {
68    metric::init(metrics, install, hooks).await?;
69    service::init(metrics, install, runtime, hooks).await?;
70    trace::init(metrics, install, runtime, hooks).await?;
71    let request_logger = logger::appender(install, metrics, hooks).await?;
72    let request_logger = Arc::new(request_logger);
73    let audit_logger = logger::appender(install, metrics, hooks).await?;
74    let audit_logger = Arc::new(Mutex::new(audit_logger));
75    let event_logger = logger::appender(install, metrics, hooks).await?;
76
77    AUDIT_LOGGER
78        .fill(audit_logger.clone())
79        .ok()
80        .expect("Audit logger already initialized");
81
82    EVENT_LOGGER
83        .set(event_logger)
84        .ok()
85        .expect("Event logger already initialized");
86
87    cleanup::cleanup_logs().await;
88
89    Ok(Loggers {
90        request_logger,
91        audit_logger,
92    })
93}
94
95/// Write the provided v3 audit log entry to the audit log using the global audit logger.
96///
97/// Returns an error if the global audit logger is not initialized.
98///
99/// The returned future completes once the audit log has been successfully queued.
100pub async fn audit_log(entry: AuditLogEntry) -> Result<(), Error> {
101    let audit_logger = AUDIT_LOGGER
102        .borrow()
103        .ok_or_else(|| Error::internal_safe("Audit logger not initialized"))?;
104
105    audit_logger
106        .lock()
107        .await
108        .feed(entry.0)
109        .await
110        .map_err(|_| Error::internal_safe("Audit logger is closed or not ready"))?;
111
112    Ok(())
113}
114
115/// Blocking variant of [audit_log].
116pub fn audit_log_blocking(entry: AuditLogEntry) -> Result<(), Error> {
117    block_on(audit_log(entry))
118}
119
120/// Writes the provided V2 event log entry using the standard logging appender without blocking.
121///
122/// If the logging appender is not initialized, this instead writes out to stdout.
123pub fn event_log(entry: EventLogV2) {
124    match EVENT_LOGGER.get() {
125        Some(event_logger) => {
126            let _ = event_logger.try_send(entry);
127        }
128        None => {
129            let mut buf = json::to_vec(&entry).unwrap();
130            buf.push(b'\n');
131            let _ = io::stdout().write_all(&buf);
132        }
133    }
134}