async_profiler_agent/reporter/
local.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use async_trait::async_trait;
5use chrono::SecondsFormat;
6use std::path::PathBuf;
7use std::time::SystemTime;
8use thiserror::Error;
9
10use crate::metadata::ReportMetadata;
11
12use super::Reporter;
13
14#[derive(Error, Debug)]
15enum LocalReporterError {
16    #[error("{0}")]
17    IoError(#[from] std::io::Error),
18}
19
20/// A reporter that reports into a directory.
21///
22/// The files are reported with the filename `yyyy-mm-ddTHH-MM-SSZ.jfr`
23#[derive(Debug)]
24pub struct LocalReporter {
25    directory: PathBuf,
26}
27
28impl LocalReporter {
29    /// Instantiate a new LocalReporter writing into the provided directory.
30    pub fn new(directory: impl Into<PathBuf>) -> Self {
31        LocalReporter {
32            directory: directory.into(),
33        }
34    }
35
36    /// Writes the jfr file to disk.
37    async fn report_profiling_data(
38        &self,
39        jfr: Vec<u8>,
40        _metadata_obj: &ReportMetadata<'_>,
41    ) -> Result<(), std::io::Error> {
42        let time: chrono::DateTime<chrono::Utc> = SystemTime::now().into();
43        let time = time
44            .to_rfc3339_opts(SecondsFormat::Secs, true)
45            .replace(":", "-");
46        tracing::debug!("reporting {time}.jfr");
47        let file_name = format!("{time}.jfr");
48        tokio::fs::write(self.directory.join(file_name), jfr).await?;
49        Ok(())
50    }
51}
52
53#[async_trait]
54impl Reporter for LocalReporter {
55    async fn report(
56        &self,
57        jfr: Vec<u8>,
58        metadata: &ReportMetadata,
59    ) -> Result<(), Box<dyn std::error::Error + Send>> {
60        self.report_profiling_data(jfr, metadata)
61            .await
62            .map_err(|e| Box::new(e) as _)
63    }
64}
65
66#[cfg(test)]
67mod test {
68    use std::path::Path;
69
70    use crate::{
71        metadata::DUMMY_METADATA,
72        reporter::{local::LocalReporter, Reporter},
73    };
74
75    #[tokio::test]
76    async fn test_local_reporter() {
77        let dir = tempfile::tempdir().unwrap();
78        let reporter = LocalReporter::new(dir.path());
79        reporter
80            .report(b"JFR".into(), &DUMMY_METADATA)
81            .await
82            .unwrap();
83        let jfr_file = std::fs::read_dir(dir.path())
84            .unwrap()
85            .flat_map(|f| f.ok())
86            .filter(|f| {
87                Path::new(&f.file_name())
88                    .extension()
89                    .is_some_and(|e| e == "jfr")
90            })
91            .next()
92            .unwrap();
93        assert_eq!(tokio::fs::read(jfr_file.path()).await.unwrap(), b"JFR");
94    }
95}