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
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    anyhow::{Context, Error},
    fidl_fuchsia_examples_inspect::{FizzBuzzRequest, FizzBuzzRequestStream},
    fuchsia_async as fasync,
    fuchsia_component::server::ServiceFs,
    fuchsia_inspect::{self as inspect, component, HistogramProperty, NumericProperty},
    fuchsia_zircon::{self as zx},
    futures::{StreamExt, TryStreamExt},
    std::sync::Arc,
    tracing::{error, info},
};

struct FizzBuzzServerMetrics {
    incoming_connection_count: inspect::UintProperty,
    closed_connection_count: inspect::UintProperty,
    request_count: inspect::UintProperty,
    request_time_histogram: inspect::UintExponentialHistogramProperty,
}

impl FizzBuzzServerMetrics {
    fn new() -> Self {
        let node = component::inspector().root().create_child("fizzbuzz_service");
        let metrics = Self {
            incoming_connection_count: node.create_uint("incoming_connection_count", 0),
            closed_connection_count: node.create_uint("closed_connection_count", 0),
            request_count: node.create_uint("request_count", 0),
            request_time_histogram: node.create_uint_exponential_histogram(
                "request_time_histogram_us",
                inspect::ExponentialHistogramParams {
                    floor: 1,
                    initial_step: 1,
                    step_multiplier: 2,
                    buckets: 16,
                },
            ),
        };
        component::inspector().root().record(node);
        metrics
    }
}

struct FizzBuzzServer {
    metrics: Arc<FizzBuzzServerMetrics>,
}

impl FizzBuzzServer {
    fn new(metrics: Arc<FizzBuzzServerMetrics>) -> Self {
        Self { metrics }
    }

    fn spawn(self, stream: FizzBuzzRequestStream) {
        fasync::Task::local(async move {
            self.metrics.incoming_connection_count.add(1);
            self.handle_request_stream(stream).await.unwrap_or_else(|e| {
                error!(?e, "Error handling fizzbuzz request stream");
            });
            self.metrics.closed_connection_count.add(1);
        })
        .detach();
    }

    async fn handle_request_stream(&self, mut stream: FizzBuzzRequestStream) -> Result<(), Error> {
        while let Some(request) = stream.try_next().await.context("serve fizzbuzz")? {
            let FizzBuzzRequest::Execute { count, responder } = request;
            self.metrics.request_count.add(1);
            let start_time = zx::Time::get_monotonic();
            responder.send(&fizzbuzz(count)).context("send execute response")?;
            let stop_time = zx::Time::get_monotonic();
            let time_micros = (stop_time - start_time).into_micros() as u64;
            self.metrics.request_time_histogram.insert(time_micros);
        }
        Ok(())
    }
}

fn fizzbuzz(n: u32) -> String {
    (1..=n)
        .into_iter()
        .map(|i| match (i % 3, i % 5) {
            (0, 0) => "FizzBuzz".to_string(),
            (0, _) => "Fizz".to_string(),
            (_, 0) => "Buzz".to_string(),
            (_, _) => format!("{}", i).to_string(),
        })
        .collect::<Vec<_>>()
        .join(" ")
}

#[fuchsia::main(logging_tags = ["inspect_rust_codelab", "fizzbuzz"])]
async fn main() -> Result<(), Error> {
    let mut fs = ServiceFs::new();

    info!("starting up...");

    let metrics = Arc::new(FizzBuzzServerMetrics::new());

    fs.dir("svc")
        .add_fidl_service(move |stream| FizzBuzzServer::new(metrics.clone()).spawn(stream));

    inspect_runtime::serve(component::inspector(), &mut fs)?;

    fs.take_and_serve_directory_handle()?;
    fs.collect::<()>().await;
    Ok(())
}