File size: 2,765 Bytes
d90101d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
use std::path::PathBuf;

use tracing::debug;
use tracing_appender::non_blocking::{self, WorkerGuard};
use tracing_subscriber::prelude::*;
use tracing_subscriber::{self, Layer, filter};

use crate::Tracker;
use crate::can_track::can_track;

pub fn init_tracing(log_path: PathBuf, tracker: Tracker) -> anyhow::Result<Guard> {
    debug!(path = %log_path.display(), "Initializing logging system in JSON format");

    // If tracking is enabled, use PostHog for logging; otherwise, use a rolling
    // file appender.
    let (writer, guard, level) = prepare_writer(log_path, tracker);

    // Create a filter that only allows logs from forge_ modules
    let filter = filter::filter_fn(|metadata| metadata.target().starts_with("forge_"));

    let fmt_layer = tracing_subscriber::fmt::layer()
        .json()
        .with_timer(tracing_subscriber::fmt::time::uptime())
        .with_thread_ids(false)
        .with_target(false)
        .with_file(true)
        .with_line_number(true)
        .with_writer(writer)
        .with_filter(filter);

    tracing_subscriber::registry()
        .with(tracing_subscriber::EnvFilter::try_from_env("FORGE_LOG").unwrap_or(level))
        .with(fmt_layer)
        .init();

    Ok(Guard(guard))
}

fn prepare_writer(
    log_path: PathBuf,
    tracker: Tracker,
) -> (
    non_blocking::NonBlocking,
    WorkerGuard,
    tracing_subscriber::EnvFilter,
) {
    let ((non_blocking, guard), env) = if can_track() {
        let append = PostHogWriter::new(tracker);
        (
            tracing_appender::non_blocking(append),
            tracing_subscriber::EnvFilter::new("forge=info"),
        )
    } else {
        let append = tracing_appender::rolling::daily(log_path, "forge.log");
        (
            tracing_appender::non_blocking(append),
            tracing_subscriber::EnvFilter::new("forge=debug"),
        )
    };
    (non_blocking, guard, env)
}

pub struct Guard(#[allow(dead_code)] WorkerGuard);

struct PostHogWriter {
    tracker: Tracker,
    runtime: tokio::runtime::Runtime,
}

impl PostHogWriter {
    pub fn new(tracker: Tracker) -> Self {
        let runtime = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .worker_threads(1)
            .build()
            .expect("Failed to create Tokio runtime");
        Self { tracker, runtime }
    }
}

impl std::io::Write for PostHogWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let tracker = self.tracker.clone();
        let event_kind = crate::EventKind::Trace(buf.to_vec());
        self.runtime.spawn(async move {
            let _ = tracker.dispatch(event_kind).await;
        });
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}