diff --git a/eden/fs/fuse/TARGETS b/eden/fs/fuse/TARGETS index a600f62bfdf77..d4d03124a3a0e 100644 --- a/eden/fs/fuse/TARGETS +++ b/eden/fs/fuse/TARGETS @@ -33,6 +33,7 @@ cpp_library( "//eden/fs/inodes:request_context", "//eden/fs/store:context", "//eden/fs/store:store", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:telemetry", "//eden/fs/utils:bufvec", "//eden/fs/utils:fs_channel_types", diff --git a/eden/fs/fuse/fuse_tester/TARGETS b/eden/fs/fuse/fuse_tester/TARGETS index 88221a3d5592d..9e13036238749 100644 --- a/eden/fs/fuse/fuse_tester/TARGETS +++ b/eden/fs/fuse/fuse_tester/TARGETS @@ -18,7 +18,7 @@ cpp_binary( "//eden/fs/privhelper:interface", "//eden/fs/privhelper:privhelper", "//eden/fs/store:store", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//eden/fs/utils:user_info", "//folly:exception", "//folly/init:init", diff --git a/eden/fs/fuse/test/TARGETS b/eden/fs/fuse/test/TARGETS index 4bb0dbfa83772..efe5da67856e2 100644 --- a/eden/fs/fuse/test/TARGETS +++ b/eden/fs/fuse/test/TARGETS @@ -14,7 +14,7 @@ cpp_unittest( "//eden/common/utils:enum", "//eden/common/utils:process_info_cache", "//eden/fs/fuse:fuse", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//eden/fs/testharness:fake_fuse", "//eden/fs/testharness:test_dispatcher", "//folly:random", diff --git a/eden/fs/inodes/TARGETS b/eden/fs/inodes/TARGETS index 527369fd9feaf..ece99eeb73111 100644 --- a/eden/fs/inodes/TARGETS +++ b/eden/fs/inodes/TARGETS @@ -34,6 +34,7 @@ cpp_library( ], exported_deps = [ "//eden/fs/store:context", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:telemetry", "//eden/fs/utils:process_access_log", "//folly/futures:core", @@ -222,6 +223,7 @@ cpp_library( "//eden/fs/takeover:takeover", "//eden/fs/telemetry:activity_buffer", "//eden/fs/telemetry:activity_recorder", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", "//eden/fs/telemetry:telemetry", "//eden/fs/utils:bufvec", diff --git a/eden/fs/inodes/lmdbcatalog/test/TARGETS b/eden/fs/inodes/lmdbcatalog/test/TARGETS index f65ac595f8666..75918065b9f9f 100644 --- a/eden/fs/inodes/lmdbcatalog/test/TARGETS +++ b/eden/fs/inodes/lmdbcatalog/test/TARGETS @@ -32,8 +32,8 @@ cpp_unittest( "//eden/fs/inodes/lmdbcatalog:lmdb_inode_catalog", "//eden/fs/inodes/overlay:serialization-cpp2-types", "//eden/fs/inodes/test:overlay_test_util", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:fake_backing_store_and_tree_builder", "//eden/fs/testharness:test_mount", "//folly/portability:gtest", diff --git a/eden/fs/inodes/sqlitecatalog/test/TARGETS b/eden/fs/inodes/sqlitecatalog/test/TARGETS index 9017b37e831f7..2612259baf1f7 100644 --- a/eden/fs/inodes/sqlitecatalog/test/TARGETS +++ b/eden/fs/inodes/sqlitecatalog/test/TARGETS @@ -16,8 +16,8 @@ cpp_unittest( "//eden/fs/inodes/sqlitecatalog:bufferedsqliteinodecatalog", "//eden/fs/inodes/sqlitecatalog:sqliteinodecatalog", "//eden/fs/inodes/test:overlay_test_util", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:fake_backing_store_and_tree_builder", "//eden/fs/testharness:test_checks", "//eden/fs/testharness:test_mount", diff --git a/eden/fs/inodes/test/TARGETS b/eden/fs/inodes/test/TARGETS index f22b55708c5ea..2c40b26b8de29 100644 --- a/eden/fs/inodes/test/TARGETS +++ b/eden/fs/inodes/test/TARGETS @@ -250,7 +250,7 @@ cpp_unittest( supports_static_listing = False, deps = [ "//eden/fs/inodes:inodes", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//folly/chrono:conv", "//folly/experimental:test_util", "//folly/portability:gtest", @@ -318,8 +318,8 @@ cpp_unittest( "//eden/fs/inodes/fscatalog:fsinodecatalog", "//eden/fs/model:testutil", "//eden/fs/service:pretty_printers", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:fake_backing_store_and_tree_builder", "//eden/fs/testharness:test_checks", "//eden/fs/testharness:test_mount", @@ -347,8 +347,8 @@ cpp_unittest( "//eden/common/testharness:temp_file", "//eden/fs/inodes:inodes", "//eden/fs/inodes/fscatalog:fsinodecatalog", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:test_util", "//folly:exception", "//folly:expected", @@ -501,8 +501,8 @@ cpp_binary( "//eden/common/utils:case_sensitivity", "//eden/fs/config:config", "//eden/fs/inodes:inodes", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//folly/init:init", "//folly/portability:gflags", ], @@ -514,8 +514,8 @@ cpp_benchmark( deps = [ "//eden/fs/config:config", "//eden/fs/inodes:inodes", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//folly:stop_watch", "//folly/init:init", "//folly/portability:gflags", @@ -529,8 +529,8 @@ cpp_benchmark( "//eden/fs/config:config", "//eden/fs/inodes:inode_catalog", "//eden/fs/inodes:inodes", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//folly:stop_watch", "//folly/init:init", "//folly/portability:gflags", diff --git a/eden/fs/journal/TARGETS b/eden/fs/journal/TARGETS index 7b6b697fedaa7..e09e9a19e9f55 100644 --- a/eden/fs/journal/TARGETS +++ b/eden/fs/journal/TARGETS @@ -15,7 +15,7 @@ cpp_library( "//eden/common/utils:path", "//eden/fs/model:model", "//eden/fs/service:thrift-streaming-cpp2-types", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//folly:function", "//folly:synchronized", ], diff --git a/eden/fs/nfs/TARGETS b/eden/fs/nfs/TARGETS index dd92a27b2a3de..dfcc2d32e077f 100644 --- a/eden/fs/nfs/TARGETS +++ b/eden/fs/nfs/TARGETS @@ -108,7 +108,7 @@ cpp_library( srcs = ["NfsDispatcher.cpp"], headers = ["NfsDispatcher.h"], deps = [ - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", ], exported_deps = [ ":dirlist", diff --git a/eden/fs/prjfs/TARGETS b/eden/fs/prjfs/TARGETS index 70d6da8dcc493..115784373a4c4 100644 --- a/eden/fs/prjfs/TARGETS +++ b/eden/fs/prjfs/TARGETS @@ -26,6 +26,7 @@ cpp_library( "//eden/fs/config:config", "//eden/fs/notifications:notifier", "//eden/fs/telemetry:log_info", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", "//eden/fs/utils:eden_error", "//eden/fs/utils:static_assert", diff --git a/eden/fs/service/TARGETS b/eden/fs/service/TARGETS index 43aea4a3500cd..4015354289540 100644 --- a/eden/fs/service/TARGETS +++ b/eden/fs/service/TARGETS @@ -450,6 +450,7 @@ cpp_library( "//eden/fs/takeover:takeover", "//eden/fs/telemetry:activity_buffer", "//eden/fs/telemetry:activity_recorder", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:telemetry", "//fb303:base_service", "//folly:executor", diff --git a/eden/fs/store/TARGETS b/eden/fs/store/TARGETS index d7d02787173d1..7155717dc3843 100644 --- a/eden/fs/store/TARGETS +++ b/eden/fs/store/TARGETS @@ -20,8 +20,8 @@ cpp_binary( "//eden/fs/config:config", "//eden/fs/service:init", "//eden/fs/service:server", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/utils:user_info", "//folly:range", "//folly:stop_watch", @@ -46,9 +46,9 @@ cpp_library( "//eden/common/utils:throw", "//eden/fs/config:config", "//eden/fs/telemetry:log_info", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", "//eden/fs/telemetry:task_trace", - "//eden/fs/telemetry:telemetry", "//fb303:service_data", "//folly:string", "//folly/futures:core", @@ -75,7 +75,7 @@ cpp_library( srcs = SQLITE_SRCS, headers = SQLITE_HEADERS, deps = [ - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//folly:string", "//folly/container:array", "//folly/logging:logging", @@ -204,7 +204,7 @@ cpp_library( "//eden/fs/model:model", "//eden/fs/model:model-fwd", "//eden/fs/service:thrift-cpp2-types", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//folly:cancellation_token", "//folly:executor", "//folly:intrusive_list", diff --git a/eden/fs/store/filter/test/TARGETS b/eden/fs/store/filter/test/TARGETS index 6470c48d74176..5cd12ff0d8977 100644 --- a/eden/fs/store/filter/test/TARGETS +++ b/eden/fs/store/filter/test/TARGETS @@ -16,8 +16,8 @@ cpp_unittest( "//eden/fs/store:store", "//eden/fs/store/filter:filtered_object_id", "//eden/fs/store/filter:glob_filter", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:fake_backing_store_and_tree_builder", "//folly:range", "//folly/executors:manual_executor", diff --git a/eden/fs/store/hg/TARGETS b/eden/fs/store/hg/TARGETS index 956e1b7a81773..0acbea0939e3d 100644 --- a/eden/fs/store/hg/TARGETS +++ b/eden/fs/store/hg/TARGETS @@ -51,6 +51,7 @@ cpp_library( "//eden/fs/config:config", "//eden/fs/service:thrift_util", "//eden/fs/telemetry:log_info", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", "//eden/fs/utils:static_assert", "//folly:executor", @@ -136,7 +137,7 @@ cpp_library( "fbsource//third-party/fmt:fmt", "//eden/common/utils:throw", "//eden/common/utils:utils", - "//eden/fs/telemetry:telemetry", + "//eden/fs/telemetry:stats", "//folly/logging:logging", ], exported_deps = [ diff --git a/eden/fs/store/hg/test/TARGETS b/eden/fs/store/hg/test/TARGETS index 5db37f917c5bf..c8f01e3643d1d 100644 --- a/eden/fs/store/hg/test/TARGETS +++ b/eden/fs/store/hg/test/TARGETS @@ -40,6 +40,7 @@ cpp_unittest( "//eden/fs/store/hg:hg_import_request_queue", "//eden/fs/store/hg:hg_proxy_hash", "//eden/fs/store/hg:hg_queued_backing_store", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:hg_repo", diff --git a/eden/fs/store/test/TARGETS b/eden/fs/store/test/TARGETS index 3af6f5418fcfd..34a19d7b08e41 100644 --- a/eden/fs/store/test/TARGETS +++ b/eden/fs/store/test/TARGETS @@ -81,8 +81,8 @@ cpp_unittest( "//eden/fs/store:rocksdb", "//eden/fs/store:sqlite", "//eden/fs/store:store", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/testharness:fake_backing_store_and_tree_builder", "//eden/fs/testharness:logging_fetch_context", "//eden/fs/testharness:stored_object", @@ -115,7 +115,7 @@ cpp_binary( "//eden/common/utils/benchharness:benchharness", "//eden/fs/model:model", "//eden/fs/store:rocksdb", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", ], ) diff --git a/eden/fs/telemetry/DurationScope.h b/eden/fs/telemetry/DurationScope.h new file mode 100644 index 0000000000000..7da0a9b31a26b --- /dev/null +++ b/eden/fs/telemetry/DurationScope.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This software may be used and distributed according to the terms of the + * GNU General Public License version 2. + */ + +#pragma once + +#include +#include + +#include +#include + +#include "eden/fs/telemetry/StatsGroup.h" + +namespace facebook::eden { + +/** + * On construction, notes the current time. On destruction, records the elapsed + * time in the specified Stats Duration. + * + * Moveable, but not copyable. + */ +template +class DurationScope { + public: + using StatsPtr = RefPtr; + + DurationScope() = delete; + + template + DurationScope(StatsPtr&& stats, StatsGroupBase::Duration T::*duration) + : stats_{std::move(stats)}, + // This use of std::function won't allocate on libstdc++, + // libc++, or Microsoft STL. All three have a couple pointers + // worth of small buffer inline storage. + updateScope_{[duration](Stats& stats, StopWatch::duration elapsed) { + stats.addDuration(duration, elapsed); + }} { + assert(stats_); + } + + template + DurationScope(const StatsPtr& stats, StatsGroupBase::Duration T::*duration) + : DurationScope{stats.copy(), duration} {} + + ~DurationScope() noexcept { + if (stats_ && updateScope_) { + try { + updateScope_(*stats_, stopWatch_.elapsed()); + } catch (const std::exception& e) { + XLOG(ERR) << "error recording duration: " << e.what(); + } + } + } + + DurationScope(DurationScope&& that) = default; + DurationScope& operator=(DurationScope&& that) = default; + + DurationScope(const DurationScope&) = delete; + DurationScope& operator=(const DurationScope&) = delete; + + private: + using StopWatch = folly::stop_watch<>; + StopWatch stopWatch_; + StatsPtr stats_; + std::function updateScope_; +}; + +} // namespace facebook::eden diff --git a/eden/fs/telemetry/EdenStats.cpp b/eden/fs/telemetry/EdenStats.cpp index 5f1cb6923d8dd..b0604ab48cf9e 100644 --- a/eden/fs/telemetry/EdenStats.cpp +++ b/eden/fs/telemetry/EdenStats.cpp @@ -7,7 +7,6 @@ #include "eden/fs/telemetry/EdenStats.h" -#include #include namespace facebook::eden { @@ -20,34 +19,4 @@ void EdenStats::flush() { fb303::ServiceData::get()->getQuantileStatMap()->flushAll(); } -StatsGroupBase::Counter::Counter(std::string_view name) - : Stat{ - name, - fb303::ExportTypeConsts::kSumCountAvgRate, - // Don't record quantiles for counters. Usually "1" is the only value - // added. Usually we care about counts and rates. - {}, - fb303::SlidingWindowPeriodConsts::kOneMinTenMinHour, - } { - // TODO: enforce the name matches the StatsGroup prefix. -} - -StatsGroupBase::Duration::Duration(std::string_view name) - : Stat{ - name, - fb303::ExportTypeConsts::kSumCountAvgRate, - fb303::QuantileConsts::kP1_P10_P50_P90_P99, - fb303::SlidingWindowPeriodConsts::kOneMinTenMinHour} { - // This should be a compile-time check but I don't know how to spell that in a - // convenient way. :) Asserting at startup in debug mode should be sufficient. - XCHECK_GT(name.size(), size_t{3}) << "duration name too short"; - XCHECK_EQ("_us", std::string_view(name.data() + name.size() - 3, 3)) - << "duration stats must end in _us"; - // TODO: enforce the name matches the StatsGroup prefix. -} - -void StatsGroupBase::Duration::addDuration(std::chrono::microseconds elapsed) { - addValue(elapsed.count()); -} - } // namespace facebook::eden diff --git a/eden/fs/telemetry/EdenStats.h b/eden/fs/telemetry/EdenStats.h index bd40fc7b93fc8..0566782c96864 100644 --- a/eden/fs/telemetry/EdenStats.h +++ b/eden/fs/telemetry/EdenStats.h @@ -9,13 +9,12 @@ #include -#include #include -#include -#include #include "eden/common/utils/RefPtr.h" #include "eden/fs/eden-config.h" +#include "eden/fs/telemetry/DurationScope.h" +#include "eden/fs/telemetry/StatsGroup.h" namespace facebook::eden { @@ -33,55 +32,6 @@ struct OverlayStats; struct InodeMapStats; struct InodeMetadataTableStats; -/** - * StatsGroupBase is a base class for a group of thread-local stats - * structures. - * - * Each StatsGroupBase object should only be used from a single thread. The - * EdenStats object should be used to maintain one StatsGroupBase object - * for each thread that needs to access/update the stats. - */ -class StatsGroupBase { - using Stat = fb303::detail::QuantileStatWrapper; - - public: - /** - * Counter is used to record events. - */ - class Counter : private Stat { - public: - explicit Counter(std::string_view name); - - using Stat::addValue; - }; - - /** - * Duration is used for stats that measure elapsed times. - * - * In general, EdenFS measures latencies in units of microseconds. - * Duration enforces that its stat names end in "_us". - */ - class Duration : private Stat { - public: - explicit Duration(std::string_view name); - - /** - * Record a duration in microseconds to the QuantileStatWrapper. Also - * increments the .count statistic. - */ - template - void addDuration(std::chrono::duration elapsed) { - // TODO: Implement a general overflow check when converting from seconds - // or milliseconds to microseconds. Fortunately, this use case deals with - // short durations. - addDuration( - std::chrono::duration_cast(elapsed)); - } - - void addDuration(std::chrono::microseconds elapsed); - }; -}; - class EdenStats : public RefCounted { public: /** @@ -201,18 +151,6 @@ EdenStats::getStatsForCurrentThread() { return *inodeMetadataTableStats_.get(); } -template -class StatsGroup : public StatsGroupBase { - public: - /** - * Statistics are often updated on a thread separate from the thread that - * started a request. Since stat objects are thread-local, we cannot hold - * pointers directly to them. Instead, we store a pointer-to-member and look - * up the calling thread's object. - */ - using DurationPtr = Duration T::*; -}; - struct FuseStats : StatsGroup { Duration lookup{"fuse.lookup_us"}; Duration forget{"fuse.forget_us"}; @@ -430,54 +368,4 @@ struct InodeMetadataTableStats : StatsGroup { Counter getMiss{"inode_metadata_table.get_miss"}; }; -/** - * On construction, notes the current time. On destruction, records the elapsed - * time in the specified StatsPtr Duration. - * - * Moveable, but not copyable. - */ -template > -class DurationScope { - public: - DurationScope() = delete; - - template - DurationScope(StatsPtr&& stats, StatsGroupBase::Duration T::*duration) - : stats_{std::move(stats)}, - // This use of std::function won't allocate on libstdc++, - // libc++, or Microsoft STL. All three have a couple pointers - // worth of small buffer inline storage. - updateScope_{[duration](Stats& stats, StopWatch::duration elapsed) { - stats.addDuration(duration, elapsed); - }} { - assert(stats_); - } - - template - DurationScope(const StatsPtr& stats, StatsGroupBase::Duration T::*duration) - : DurationScope{stats.copy(), duration} {} - - ~DurationScope() noexcept { - if (stats_ && updateScope_) { - try { - updateScope_(*stats_, stopWatch_.elapsed()); - } catch (const std::exception& e) { - XLOG(ERR) << "error recording duration: " << e.what(); - } - } - } - - DurationScope(DurationScope&& that) = default; - DurationScope& operator=(DurationScope&& that) = default; - - DurationScope(const DurationScope&) = delete; - DurationScope& operator=(const DurationScope&) = delete; - - private: - using StopWatch = folly::stop_watch<>; - StopWatch stopWatch_; - StatsPtr stats_; - std::function updateScope_; -}; - } // namespace facebook::eden diff --git a/eden/fs/telemetry/StatsGroup.cpp b/eden/fs/telemetry/StatsGroup.cpp new file mode 100644 index 0000000000000..5bdc0dc095a61 --- /dev/null +++ b/eden/fs/telemetry/StatsGroup.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This software may be used and distributed according to the terms of the + * GNU General Public License version 2. + */ + +#include "eden/fs/telemetry/StatsGroup.h" + +#include + +#include + +namespace facebook::eden { + +StatsGroupBase::Counter::Counter(std::string_view name) + : Stat{ + name, + fb303::ExportTypeConsts::kSumCountAvgRate, + // Don't record quantiles for counters. Usually "1" is the only value + // added. Usually we care about counts and rates. + {}, + fb303::SlidingWindowPeriodConsts::kOneMinTenMinHour, + } { + // TODO: enforce the name matches the StatsGroup prefix. +} + +StatsGroupBase::Duration::Duration(std::string_view name) + : Stat{ + name, + fb303::ExportTypeConsts::kSumCountAvgRate, + fb303::QuantileConsts::kP1_P10_P50_P90_P99, + fb303::SlidingWindowPeriodConsts::kOneMinTenMinHour} { + // This should be a compile-time check but I don't know how to spell that in a + // convenient way. :) Asserting at startup in debug mode should be sufficient. + XCHECK_GT(name.size(), size_t{3}) << "duration name too short"; + XCHECK_EQ("_us", std::string_view(name.data() + name.size() - 3, 3)) + << "duration stats must end in _us"; + // TODO: enforce the name matches the StatsGroup prefix. +} + +void StatsGroupBase::Duration::addDuration(std::chrono::microseconds elapsed) { + addValue(elapsed.count()); +} + +} // namespace facebook::eden diff --git a/eden/fs/telemetry/StatsGroup.h b/eden/fs/telemetry/StatsGroup.h new file mode 100644 index 0000000000000..353940cb259b3 --- /dev/null +++ b/eden/fs/telemetry/StatsGroup.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This software may be used and distributed according to the terms of the + * GNU General Public License version 2. + */ + +#pragma once + +#include + +#include +#include + +namespace facebook::eden { + +/** + * StatsGroupBase is a base class for a group of thread-local stats + * structures. + * + * Each StatsGroupBase object should only be used from a single thread. The + * EdenStats object should be used to maintain one StatsGroupBase object + * for each thread that needs to access/update the stats. + */ +class StatsGroupBase { + using Stat = fb303::detail::QuantileStatWrapper; + + public: + /** + * Counter is used to record events. + */ + class Counter : private Stat { + public: + explicit Counter(std::string_view name); + + using Stat::addValue; + }; + + /** + * Duration is used for stats that measure elapsed times. + * + * In general, EdenFS measures latencies in units of microseconds. + * Duration enforces that its stat names end in "_us". + */ + class Duration : private Stat { + public: + explicit Duration(std::string_view name); + + /** + * Record a duration in microseconds to the QuantileStatWrapper. Also + * increments the .count statistic. + */ + template + void addDuration(std::chrono::duration elapsed) { + // TODO: Implement a general overflow check when converting from seconds + // or milliseconds to microseconds. Fortunately, this use case deals with + // short durations. + addDuration( + std::chrono::duration_cast(elapsed)); + } + + void addDuration(std::chrono::microseconds elapsed); + }; +}; + +template +class StatsGroup : public StatsGroupBase { + public: + /** + * Statistics are often updated on a thread separate from the thread that + * started a request. Since stat objects are thread-local, we cannot hold + * pointers directly to them. Instead, we store a pointer-to-member and look + * up the calling thread's object. + */ + using DurationPtr = Duration T::*; +}; + +} // namespace facebook::eden diff --git a/eden/fs/telemetry/TARGETS b/eden/fs/telemetry/TARGETS index 64e0e5bf96626..c6fa97abeda16 100644 --- a/eden/fs/telemetry/TARGETS +++ b/eden/fs/telemetry/TARGETS @@ -3,13 +3,31 @@ load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library") oncall("scm_client_infra") cpp_library( - name = "telemetry", + name = "stats", srcs = [ "EdenStats.cpp", + ], + headers = [ + "EdenStats.h", + ], + exported_deps = [ + ":telemetry", + "//eden/common/utils:ref_ptr", + "//eden/fs:config", + "//folly:thread_local", + ], +) + +cpp_library( + name = "telemetry", + srcs = [ "RequestMetricsScope.cpp", + "StatsGroup.cpp", "Tracing.cpp", ], headers = [ + "DurationScope.h", + "StatsGroup.h", "TraceBus.h", "TraceBus-inl.h", ], @@ -18,9 +36,7 @@ cpp_library( "//folly:string", ], exported_deps = [ - "//eden/common/utils:ref_ptr", "//eden/common/utils:utils", - "//eden/fs:config", "//fb303/detail:quantile_stat_wrappers", "//folly:clock_gettime_wrappers", "//folly:range", @@ -121,7 +137,7 @@ cpp_library( "NullStructuredLogger.h", ], deps = [ - ":telemetry", + ":stats", "//eden/common/telemetry:scribe_logger", "//eden/fs/config:config", "//fb303:service_data", diff --git a/eden/fs/testharness/TARGETS b/eden/fs/testharness/TARGETS index ce40061aa347f..3c0fad6c7e958 100644 --- a/eden/fs/testharness/TARGETS +++ b/eden/fs/testharness/TARGETS @@ -27,8 +27,8 @@ cpp_library( "//eden/fs/store:backing_store_interface", "//eden/fs/store:store", "//eden/fs/telemetry:hive_logger", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/utils:eden_error", "//eden/fs/utils:user_info", "//folly:file_util", @@ -302,8 +302,8 @@ cpp_library( "//eden/fs/inodes:inodes", "//eden/fs/notifications:command_notifier", "//eden/fs/telemetry:hive_logger", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//eden/fs/utils:user_info", "//folly/executors:manual_executor", ], diff --git a/eden/integration/helpers/TARGETS b/eden/integration/helpers/TARGETS index c7a2ba041eb35..356c1867d9d9a 100644 --- a/eden/integration/helpers/TARGETS +++ b/eden/integration/helpers/TARGETS @@ -62,8 +62,8 @@ cpp_binary( "//eden/common/utils:utils", "//eden/fs/model:model", "//eden/fs/store:rocksdb", + "//eden/fs/telemetry:stats", "//eden/fs/telemetry:structured_logger", - "//eden/fs/telemetry:telemetry", "//folly/init:init", "//folly/logging:init", "//folly/logging:logging",