send_trace_to_jaeger/
main.rs

1use std::{
2    env::args,
3    fs::File,
4    io::{self, BufRead},
5    path::Path,
6};
7
8use reqwest::blocking::Client;
9use serde_json::{Map, Number, Value};
10
11/// Read individual lines from a file.
12fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
13where
14    P: AsRef<Path>,
15{
16    let file = File::open(filename)?;
17    Ok(io::BufReader::new(file).lines())
18}
19
20/// Log the url to view the trace in the browser.
21fn log_web_url(jaeger_web_ui_url: &str, trace_id: &str) {
22    println!("Jaeger trace will be available on {jaeger_web_ui_url}/trace/{trace_id}")
23}
24
25/// Send trace JSON to Jaeger using ZipKin API.
26fn send_json_to_zipkin(zipkin_api: &str, value: String) {
27    let client = Client::new();
28
29    let res = client
30        .post(zipkin_api)
31        .header("Content-Type", "application/json")
32        .body(value)
33        .send()
34        .expect("Failed to send request");
35
36    if !res.status().is_success() {
37        println!("body = {:?}", res.text());
38    }
39}
40
41// function to append zero to a number until 16 characters
42fn pad_zeros(num: u64) -> String {
43    let mut num_str = num.to_string();
44    while num_str.len() < 16 {
45        num_str = format!("0{num_str}");
46    }
47    num_str
48}
49
50fn main() {
51    let service_name = "nextjs";
52    let ipv4 = "127.0.0.1";
53    let port = 9411;
54    let zipkin_url = format!("http://{ipv4}:{port}");
55    let jaeger_web_ui_url = format!("http://{ipv4}:16686");
56    let zipkin_api = format!("{zipkin_url}/api/v2/spans");
57    let mut logged_url = false;
58
59    let mut local_endpoint = Map::new();
60    local_endpoint.insert(
61        "serviceName".to_string(),
62        Value::String(service_name.to_string()),
63    );
64    local_endpoint.insert("ipv4".to_string(), Value::String(ipv4.to_string()));
65    local_endpoint.insert("port".to_string(), Value::Number(Number::from(port)));
66
67    let first_arg = args().nth(1).expect("Please provide a file name");
68
69    if let Ok(lines) = read_lines(first_arg) {
70        for json_to_parse in lines.map_while(Result::ok) {
71            let v = match serde_json::from_str::<Vec<Value>>(&json_to_parse) {
72                Ok(v) => v
73                    .into_iter()
74                    .map(|mut data| {
75                        if !logged_url {
76                            log_web_url(&jaeger_web_ui_url, data["traceId"].as_str().unwrap());
77                            logged_url = true;
78                        }
79                        data["localEndpoint"] = Value::Object(local_endpoint.clone());
80
81                        data["id"] = Value::String(pad_zeros(data["id"].as_u64().unwrap()));
82                        if data["parentId"] != Value::Null {
83                            data["parentId"] =
84                                Value::String(pad_zeros(data["parentId"].as_u64().unwrap()));
85                        }
86
87                        if let Some(tags) = data["tags"].as_object_mut() {
88                            for (_, value) in tags.iter_mut() {
89                                if value.is_boolean() {
90                                    let bool_val = value.as_bool().unwrap();
91                                    *value = serde_json::Value::String(bool_val.to_string());
92                                }
93                            }
94                        }
95
96                        data
97                    })
98                    .collect::<Value>(),
99                Err(e) => {
100                    println!("{e}");
101                    continue;
102                }
103            };
104
105            let json_map = serde_json::to_string(&v).expect("Failed to serialize");
106
107            // println!("{:}", json_map);
108
109            send_json_to_zipkin(&zipkin_api, json_map);
110        }
111    }
112}