witchcraft_server/logging/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2021 Palantir Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Logging APIs
use crate::extensions::AuditLogEntry;
use crate::logging::api::{AuditLogV3, EventLogV2, RequestLogV2};
use crate::shutdown_hooks::ShutdownHooks;
use conjure_error::Error;
use conjure_serde::json;
use futures::executor::block_on;
use futures::SinkExt;
use lazycell::AtomicLazyCell;
pub(crate) use logger::Appender;
use once_cell::sync::OnceCell;
use refreshable::Refreshable;
use std::io;
use std::io::Write as _;
use std::sync::Arc;
use tokio::sync::Mutex;
use witchcraft_metrics::MetricRegistry;
use witchcraft_server_config::install::InstallConfig;
use witchcraft_server_config::runtime::LoggingConfig;

/// Conjure-generated type definitions for log formats.
#[allow(warnings)]
#[rustfmt::skip]
pub mod api;
mod cleanup;
mod format;
mod logger;
pub mod mdc;
mod metric;
mod service;
mod trace;

pub(crate) static AUDIT_LOGGER: AtomicLazyCell<Arc<Mutex<Appender<AuditLogV3>>>> =
    AtomicLazyCell::NONE;

static EVENT_LOGGER: OnceCell<Appender<EventLogV2>> = OnceCell::new();

pub(crate) const REQUEST_ID_KEY: &str = "_requestId";
pub(crate) const SAMPLED_KEY: &str = "_sampled";

pub(crate) struct Loggers {
    pub request_logger: Arc<Appender<RequestLogV2>>,
    pub audit_logger: Arc<Mutex<Appender<AuditLogV3>>>,
}

pub(crate) fn early_init() {
    service::early_init()
}

pub(crate) async fn init(
    metrics: &Arc<MetricRegistry>,
    install: &InstallConfig,
    runtime: &Refreshable<LoggingConfig, Error>,
    hooks: &mut ShutdownHooks,
) -> Result<Loggers, Error> {
    metric::init(metrics, install, hooks).await?;
    service::init(metrics, install, runtime, hooks).await?;
    trace::init(metrics, install, runtime, hooks).await?;
    let request_logger = logger::appender(install, metrics, hooks).await?;
    let request_logger = Arc::new(request_logger);
    let audit_logger = logger::appender(install, metrics, hooks).await?;
    let audit_logger = Arc::new(Mutex::new(audit_logger));
    let event_logger = logger::appender(install, metrics, hooks).await?;

    AUDIT_LOGGER
        .fill(audit_logger.clone())
        .ok()
        .expect("Audit logger already initialized");

    EVENT_LOGGER
        .set(event_logger)
        .ok()
        .expect("Event logger already initialized");

    cleanup::cleanup_logs().await;

    Ok(Loggers {
        request_logger,
        audit_logger,
    })
}

/// Write the provided v3 audit log entry to the audit log using the global audit logger.
///
/// Returns an error if the global audit logger is not initialized.
///
/// The returned future completes once the audit log has been successfully queued.
pub async fn audit_log(entry: AuditLogEntry) -> Result<(), Error> {
    let audit_logger = AUDIT_LOGGER
        .borrow()
        .ok_or_else(|| Error::internal_safe("Audit logger not initialized"))?;

    audit_logger
        .lock()
        .await
        .feed(entry.0)
        .await
        .map_err(|_| Error::internal_safe("Audit logger is closed or not ready"))?;

    Ok(())
}

/// Blocking variant of [audit_log].
pub fn audit_log_blocking(entry: AuditLogEntry) -> Result<(), Error> {
    block_on(audit_log(entry))
}

/// Writes the provided V2 event log entry using the standard logging appender without blocking.
///
/// If the logging appender is not initialized, this instead writes out to stdout.
pub fn event_log(entry: EventLogV2) {
    match EVENT_LOGGER.get() {
        Some(event_logger) => {
            let _ = event_logger.try_send(entry);
        }
        None => {
            let mut buf = json::to_vec(&entry).unwrap();
            buf.push(b'\n');
            let _ = io::stdout().write_all(&buf);
        }
    }
}