• Stars
    star
    602
  • Rank 74,409 (Top 2 %)
  • Language
    Swift
  • License
    Apache License 2.0
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Metrics API for Swift

SwiftMetrics

A Metrics API package for Swift.

Almost all production server software needs to emit metrics information for observability. Because it's unlikely that all parties can agree on one specific metrics backend implementation, this API is designed to establish a standard that can be implemented by various metrics libraries which then post the metrics data to backends like Prometheus, Graphite, publish over statsd, write to disk, etc.

This is the beginning of a community-driven open-source project actively seeking contributions, be it code, documentation, or ideas. Apart from contributing to SwiftMetrics itself, we need metrics compatible libraries which send the metrics over to backend such as the ones mentioned above. What SwiftMetrics provides today is covered in the API docs, but it will continue to evolve with community input.

Getting started

If you have a server-side Swift application, or maybe a cross-platform (e.g. Linux, macOS) application or library, and you would like to emit metrics, targeting this metrics API package is a great idea. Below you'll find all you need to know to get started.

Adding the dependency

To add a dependency on the metrics API package, you need to declare it in your Package.swift:

// swift-metrics 1.x and 2.x are almost API compatible, so most clients should use
.package(url: "https://github.com/apple/swift-metrics.git", "1.0.0" ..< "3.0.0"),

and to your application/library target, add "Metrics" to your dependencies:

.target(
    name: "BestExampleApp",
    dependencies: [
        // ... 
        .product(name: "Metrics", package: "swift-metrics"),
    ]
),

Emitting metrics information

// 1) let's import the metrics API package
import Metrics

// 2) we need to create a concrete metric object, the label works similarly to a `DispatchQueue` label
let counter = Counter(label: "com.example.BestExampleApp.numberOfRequests")

// 3) we're now ready to use it
counter.increment()

Selecting a metrics backend implementation (applications only)

Note: If you are building a library, you don't need to concern yourself with this section. It is the end users of your library (the applications) who will decide which metrics backend to use. Libraries should never change the metrics implementation as that is something owned by the application.

SwiftMetrics only provides the metrics system API. As an application owner, you need to select a metrics backend (such as the ones mentioned above) to make the metrics information useful.

Selecting a backend is done by adding a dependency on the desired backend client implementation and invoking the MetricsSystem.bootstrap function at the beginning of the program:

MetricsSystem.bootstrap(SelectedMetricsImplementation())

This instructs the MetricsSystem to install SelectedMetricsImplementation (actual name will differ) as the metrics backend to use.

As the API has just launched, not many implementations exist yet. If you are interested in implementing one see the "Implementing a metrics backend" section below explaining how to do so. List of existing SwiftMetrics API compatible libraries:

Swift Metrics Extras

You may also be interested in some "extra" modules which are collected in the Swift Metrics Extras repository.

Detailed design

Architecture

We believe that for the Swift on Server ecosystem, it's crucial to have a metrics API that can be adopted by anybody so a multitude of libraries from different parties can all provide metrics information. More concretely this means that we believe all the metrics events from all libraries should end up in the same place, be one of the backends mentioned above or wherever else the application owner may choose.

In the real world, there are so many opinions over how exactly a metrics system should behave, how metrics should be aggregated and calculated, and where/how to persist them. We think it's not feasible to wait for one metrics package to support everything that a specific deployment needs while still being simple enough to use and remain performant. That's why we decided to split the problem into two:

  1. a metrics API
  2. a metrics backend implementation

This package only provides the metrics API itself, and therefore, SwiftMetrics is a "metrics API package." SwiftMetrics can be configured (using MetricsSystem.bootstrap) to choose any compatible metrics backend implementation. This way, packages can adopt the API, and the application can choose any compatible metrics backend implementation without requiring any changes from any of the libraries.

This API was designed with the contributors to the Swift on Server community and approved by the SSWG (Swift Server Work Group) to the "sandbox level" of the SSWG's incubation process.

pitch | discussion | feedback

Metric types

The API supports four metric types:

Counter: A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.

counter.increment(by: 100)

Gauge: A Gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Gauges are typically used for measured values like temperatures or current memory usage, but also "counts" that can go up and down, like the number of active threads. Gauges are modeled as a Recorder with a sample size of 1 that does not perform any aggregation.

gauge.record(100)

Meter: A Meter is similar to Gauge - a metric that represents a single numerical value that can arbitrarily go up and down. Meters are typically used for measured values like temperatures or current memory usage, but also "counts" that can go up and down, like the number of active threads. Unlike Gauge, Meter also supports atomic incerements and decerements.

meter.record(100)

Recorder: A recorder collects observations within a time window (usually things like response sizes) and can provide aggregated information about the data sample, for example count, sum, min, max and various quantiles.

recorder.record(100)

Timer: A timer collects observations within a time window (usually things like request duration) and provides aggregated information about the data sample, for example min, max and various quantiles. It is similar to a Recorder but specialized for values that represent durations.

timer.recordMilliseconds(100)

Implementing a metrics backend (e.g. Prometheus client library)

Note: Unless you need to implement a custom metrics backend, everything in this section is likely not relevant, so please feel free to skip.

As seen above, each constructor for Counter, Gauge, Meter, Recorder and Timer provides a metric object. This uncertainty obscures the selected metrics backend calling these constructors by design. Each application can select and configure its desired backend. The application sets up the metrics backend it wishes to use. Configuring the metrics backend is straightforward:

let metricsImplementation = MyFavoriteMetricsImplementation()
MetricsSystem.bootstrap(metricsImplementation)

This instructs the MetricsSystem to install MyFavoriteMetricsImplementation as the metrics backend (MetricsFactory) to use. This should only be done once at the beginning of the program.

Given the above, an implementation of a metric backend needs to conform to protocol MetricsFactory:

public protocol MetricsFactory {
    func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler
    func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler
    func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler    
    func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler

    func destroyCounter(_ handler: CounterHandler)
    func destroyMeter(_ handler: MeterHandler)
    func destroyRecorder(_ handler: RecorderHandler)
    func destroyTimer(_ handler: TimerHandler)
}

The MetricsFactory is responsible for instantiating the concrete metrics classes that capture the metrics and perform aggregation and calculation of various quantiles as needed.

Counter

public protocol CounterHandler: AnyObject {
    func increment(by: Int64)
    func reset()
}

Meter

public protocol MeterHandler: AnyObject {
    func set(_ value: Int64)
    func set(_ value: Double)
    func increment(by: Double)
    func decrement(by: Double)
}

Recorder

public protocol RecorderHandler: AnyObject {
    func record(_ value: Int64)
    func record(_ value: Double)
}

Timer

public protocol TimerHandler: AnyObject {
    func recordNanoseconds(_ duration: Int64)
}

Dealing with Overflows

Implementation of metric objects that deal with integers, like Counter and Timer should be careful with overflow. The expected behavior is to cap at .max, and never crash the program due to overflow . For example:

class ExampleCounter: CounterHandler {
    var value: Int64 = 0
    func increment(by amount: Int64) {
        let result = self.value.addingReportingOverflow(amount)
        if result.overflow {
            self.value = Int64.max
        } else {
            self.value = result.partialValue
        }
    }
}

Full example

Here is a full, but contrived, example of an in-memory implementation:

class SimpleMetricsLibrary: MetricsFactory {
    init() {}

    func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
        return ExampleCounter(label, dimensions)
    }

    func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
        return ExampleMeter(label, dimensions)
    }

    func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
        return ExampleRecorder(label, dimensions, aggregate)
    }

    func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
        return ExampleTimer(label, dimensions)
    }

    // implementation is stateless, so nothing to do on destroy calls
    func destroyCounter(_ handler: CounterHandler) {}
    func destroyMeter(_ handler: TimerHandler) {}
    func destroyRecorder(_ handler: RecorderHandler) {}    
    func destroyTimer(_ handler: TimerHandler) {}

    private class ExampleCounter: CounterHandler {
        init(_: String, _: [(String, String)]) {}

        let lock = NSLock()
        var value: Int64 = 0
        func increment(by amount: Int64) {
            self.lock.withLock {
                self.value += amount
            }
        }

        func reset() {
            self.lock.withLock {
                self.value = 0
            }
        }
    }

    private class ExampleMeter: MeterHandler {
        init(_: String, _: [(String, String)]) {}

        let lock = NSLock()
        var _value: Double = 0

        func set(_ value: Int64) {
            self.set(Double(value))
        }

        func set(_ value: Double) {
            self.lock.withLock { _value = value }
        }

        func increment(by value: Double) {
            self.lock.withLock { self._value += value }
        }

        func decrement(by value: Double) {
            self.lock.withLock { self._value -= value }
        }
    }

    private class ExampleRecorder: RecorderHandler {
        init(_: String, _: [(String, String)], _: Bool) {}

        private let lock = NSLock()
        var values = [(Int64, Double)]()
        func record(_ value: Int64) {
            self.record(Double(value))
        }

        func record(_ value: Double) {
            // TODO: sliding window
            lock.withLock {
                values.append((Date().nanoSince1970, value))
                self._count += 1
                self._sum += value
                self._min = Swift.min(self._min, value)
                self._max = Swift.max(self._max, value)
            }
        }

        var _sum: Double = 0
        var sum: Double {
            return self.lock.withLock { _sum }
        }

        private var _count: Int = 0
        var count: Int {
            return self.lock.withLock { _count }
        }

        private var _min: Double = 0
        var min: Double {
            return self.lock.withLock { _min }
        }

        private var _max: Double = 0
        var max: Double {
            return self.lock.withLock { _max }
        }
    }

    private class ExampleTimer: TimerHandler {
        init(_: String, _: [(String, String)]) {}

        let lock = NSLock()
        var _value: Int64 = 0

        func recordNanoseconds(_ duration: Int64) {
            self.lock.withLock { _value = duration }
        }
    }
}

Security

Please see SECURITY.md for details on the security process.

Getting involved

Do not hesitate to get in touch as well, over on https://forums.swift.org/c/server

More Repositories

1

swift

The Swift Programming Language
C++
66,491
star
2

ml-stable-diffusion

Stable Diffusion with Core ML on Apple Silicon
Python
16,831
star
3

swift-evolution

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
Markdown
15,085
star
4

foundationdb

FoundationDB - the open source, distributed, transactional key-value store
C++
14,444
star
5

turicreate

Turi Create simplifies the development of custom machine learning models.
C++
11,197
star
6

darwin-xnu

The Darwin Kernel (mirror). This repository is a pure mirror and contributions are currently not accepted via pull-requests, please submit your contributions via https://developer.apple.com/bug-reporting/
C
10,558
star
7

pkl

A configuration as code language with rich validation and tooling.
Java
10,223
star
8

swift-package-manager

The Package Manager for the Swift Programming Language
Swift
9,637
star
9

ml-ferret

Python
8,415
star
10

swift-nio

Event-driven network application framework for high performance protocol servers & clients, non-blocking.
Swift
7,274
star
11

corenet

CoreNet: A library for training deep neural networks
Jupyter Notebook
6,968
star
12

swift-algorithms

Commonly used sequence and collection algorithms for Swift
Swift
5,885
star
13

swift-corelibs-foundation

The Foundation Project, providing core utilities, internationalization, and OS independence
C
5,269
star
14

swift-protobuf

Plugin and runtime library for using protobuf with Swift
Swift
4,561
star
15

coremltools

Core ML tools contain supporting tools for Core ML model conversion, editing, and validation.
Python
4,401
star
16

password-manager-resources

A place for creators and users of password managers to collaborate on resources to make password management better.
JavaScript
4,144
star
17

ml-mgie

Python
3,853
star
18

tensorflow_macos

TensorFlow for macOS 11.0+ accelerated using Apple's ML Compute framework.
Shell
3,672
star
19

swift-collections

Commonly used data structures for Swift
Swift
3,651
star
20

ml-depth-pro

Depth Pro: Sharp Monocular Metric Depth in Less Than a Second.
Python
3,436
star
21

swift-argument-parser

Straightforward, type-safe argument parsing for Swift
Swift
3,289
star
22

sourcekit-lsp

Language Server Protocol implementation for Swift and C-based languages
Swift
3,160
star
23

swift-syntax

A set of Swift libraries for parsing, inspecting, generating, and transforming Swift source code.
Swift
3,064
star
24

swift-log

A Logging API for Swift
Swift
2,931
star
25

swift-async-algorithms

Async Algorithms for Swift
Swift
2,895
star
26

swift-markdown

A Swift package for parsing, building, editing, and analyzing Markdown documents.
Swift
2,669
star
27

ml-ane-transformers

Reference implementation of the Transformer architecture optimized for Apple Neural Engine (ANE)
Python
2,527
star
28

swift-corelibs-libdispatch

The libdispatch Project, (a.k.a. Grand Central Dispatch), for concurrency on multicore hardware
C
2,467
star
29

HomeKitADK

C
2,456
star
30

swift-format

Formatting technology for Swift source code
Swift
2,341
star
31

swift-foundation

The Foundation project
Swift
2,302
star
32

homebrew-apple

Ruby
2,240
star
33

cups

Apple CUPS Sources
C
1,926
star
34

axlearn

An Extensible Deep Learning Library
Python
1,840
star
35

ml-fastvit

This repository contains the official implementation of the research paper, "FastViT: A Fast Hybrid Vision Transformer using Structural Reparameterization" ICCV 2023
Python
1,826
star
36

ml-cvnets

CVNets: A library for training computer vision networks
Python
1,777
star
37

sample-food-truck

SwiftUI sample code from WWDC22
Swift
1,738
star
38

swift-numerics

Advanced mathematical types and functions for Swift
Swift
1,669
star
39

swift-book

The Swift Programming Language book
Markdown
1,666
star
40

ml-4m

4M: Massively Multimodal Masked Modeling
Python
1,590
star
41

swift-testing

A modern, expressive testing package for Swift
Swift
1,582
star
42

ml-hypersim

Hypersim: A Photorealistic Synthetic Dataset for Holistic Indoor Scene Understanding
Python
1,495
star
43

swift-crypto

Open-source implementation of a substantial portion of the API of Apple CryptoKit suitable for use on Linux platforms.
C
1,441
star
44

swift-openapi-generator

Generate Swift client and server code from an OpenAPI document.
Swift
1,423
star
45

swift-docker

Docker Official Image packaging for Swift
Dockerfile
1,331
star
46

ml-neuman

Official repository of NeuMan: Neural Human Radiance Field from a Single Video (ECCV 2022)
Python
1,256
star
47

swift-system

Low-level system calls and types for Swift
Swift
1,166
star
48

swift-docc

Documentation compiler that produces rich API reference documentation and interactive tutorials for your Swift framework or package.
Swift
1,140
star
49

swift-corelibs-xctest

The XCTest Project, A Swift core library for providing unit test support
Swift
1,138
star
50

swift-llbuild

A low-level build system, used by Xcode and the Swift Package Manager
C++
1,072
star
51

swift-atomics

Low-level atomic operations for Swift
Swift
1,050
star
52

servicetalk

A networking framework that evolves with your application
Java
910
star
53

swift-http-types

Version-independent HTTP currency types for Swift
Swift
902
star
54

swift-llvm

LLVM
813
star
55

swift-driver

Swift compiler driver reimplementation in Swift
Swift
784
star
56

swift-protobuf-plugin

Moved to apple/swift-protobuf
755
star
57

unityplugins

C#
721
star
58

swift-embedded-examples

A collection of example projects using Embedded Swift
Swift
713
star
59

ml-mobileone

This repository contains the official implementation of the research paper, "An Improved One millisecond Mobile Backbone".
Swift
709
star
60

ml-aim

This repository provides the code and model checkpoints of the research paper: Scalable Pre-training of Large Autoregressive Image Models
Python
693
star
61

swift-lldb

This is the version of LLDB that supports the Swift programming language & REPL.
C++
674
star
62

swift-clang

C++
672
star
63

ml-gaudi

611
star
64

ml-mobileclip

This repository contains the official implementation of the research paper, "MobileCLIP: Fast Image-Text Models through Multi-Modal Reinforced Training" CVPR 2024
Python
605
star
65

swift-distributed-actors

Peer-to-peer cluster implementation for Swift Distributed Actors
Swift
591
star
66

ARKitScenes

This repo accompanies the research paper, ARKitScenes - A Diverse Real-World Dataset for 3D Indoor Scene Understanding Using Mobile RGB-D Data and contains the data, scripts to visualize and process assets, and training code described in our paper.
Python
589
star
67

device-management

Device management schema data for MDM.
580
star
68

sample-backyard-birds

Swift
544
star
69

ml-facelit

Official repository of FaceLit: Neural 3D Relightable Faces (CVPR 2023)
Python
472
star
70

ccs-calendarserver

The Calendar and Contacts Server.
Python
470
star
71

swift-3-api-guidelines-review

Swift
455
star
72

swift-org-website

Swift.org website
SCSS
450
star
73

GCGC

Jupyter Notebook
438
star
74

ml-mdm

Train high-quality text-to-image diffusion models in a data & compute efficient manner
Python
433
star
75

swift-nio-http2

HTTP/2 support for SwiftNIO
Swift
405
star
76

swift-tools-support-core

Contains common infrastructural code for both SwiftPM and llbuild.
Swift
390
star
77

swift-nio-ssh

SwiftNIO SSH is a programmatic implementation of SSH using SwiftNIO
Swift
389
star
78

swift-playdate-examples

An Embedded Swift game running on Playdate by Panic
Swift
386
star
79

swift-nio-ssl

TLS Support for SwiftNIO, based on BoringSSL.
C
345
star
80

ml-gmpi

[ECCV 2022, Oral Presentation] Official PyTorch implementation of GMPI
Python
339
star
81

example-package-dealer

Example package for use with the Swift Package Manager
Swift
335
star
82

security-pcc

Private Cloud Compute (PCC)
Swift
334
star
83

swift-collections-benchmark

A benchmarking tool for Swift Collection algorithms
Swift
333
star
84

swift-homomorphic-encryption

Homomorphic Encryption library and applications in Swift
Swift
330
star
85

example-package-playingcard

Example package for use with the Swift Package Manager
Swift
323
star
86

indexstore-db

Index database library for use with sourcekit-lsp
C++
315
star
87

swift-docc-render

Web renderer for Swift-DocC documentation.
JavaScript
307
star
88

ml-hierarchical-confusion-matrix

Neo: Hierarchical Confusion Matrix Visualization (CHI 2022)
TypeScript
302
star
89

swift-docc-plugin

Swift Package Manager command plugin for Swift-DocC
Swift
301
star
90

swift-migration-guide

Markdown
294
star
91

ml-sigma-reparam

Python
292
star
92

pfl-research

Simulation framework for accelerating research in Private Federated Learning
Jupyter Notebook
289
star
93

ml-gsn

Python
284
star
94

swift-llbuild2

A fresh take on a low-level build system API.
Swift
281
star
95

swift-source-compat-suite

The infrastructure and project index comprising the Swift source compatibility suite.
Python
280
star
96

swift-xcode-playground-support

Logging and communication to allow Swift toolchains to communicate with Xcode.
Swift
279
star
97

sample-cloudkit-sharing

Swift
275
star
98

swift-experimental-string-processing

An early experimental general-purpose pattern matching engine for Swift.
Swift
270
star
99

swift-matter-examples

An Embedded Swift Matter application running on ESP32-C6
Swift
266
star
100

pkl-go

Pkl bindings for the Go programming language
Go
263
star