Swift System Metrics

repository·main·Indexed 41 days ago

https://github.com/apple/swift-system-metrics

A library for reporting process-level system metrics—such as memory usage, CPU time, and file descriptors—to the Swift Metrics ecosystem. It provides the SystemMetricsMonitor to collect metrics like process_cpu_seconds_total and process_resident_memory_bytes, and is designed for integration with Swift Service Lifecycle for background monitoring in applications and tools.

Tokens
5.6K
Snippets
19
Records
26
Agent score
50%

What's inside Swift System Metrics

  1. How to rename metric labels

    main
    If your metrics backend requires different label names than the defaults provided by SystemMetricsMonitor, do not modify the library. Instead, use MappingMetricsFactory from the swift-metrics package to intercept and rename the labels before they reach your backend.
  2. Install Swift System Metrics via Swift Package Manager

    main

    To use Swift System Metrics in your application, add it as a dependency in your Package.swift file.

    Note: This package is intended for use in tools and applications directly, rather than being used as a dependency within a library. If you are building a library and want to report metrics, use the Swift Metrics API directly.

    // Add the package dependency
    .package(url: "https://github.com/apple/swift-system-metrics", from: "1.0.0")
    
    // Add the product to your target
    .product(name: "SystemMetrics", package: "swift-system-metrics")
  3. Quick start: Monitor process metrics with SystemMetricsMonitor

    main

    To collect and report process-level metrics (like CPU and memory), create a SystemMetricsMonitor and include it in a ServiceGroup (from swift-service-lifecycle). This ensures the monitor runs periodically as a background service. You must also bootstrap a MetricsSystem implementation to receive the reported data.

    import SystemMetrics
    import ServiceLifecycle
    import Metrics
    import Logging
    
    @main
    struct Application {
      static func main() async throws {
        let logger = Logger(label: "Application")
        
        // 1. Bootstrap your metrics backend
        let metrics = MyMetricsBackendImplementation()
        MetricsSystem.bootstrap(metrics)
    
        let service = FooService()
        
        // 2. Create the monitor service
        let systemMetricsMonitor = SystemMetricsMonitor(logger: logger)
        
        // 3. Run the monitor within a ServiceGroup
        let serviceGroup = ServiceGroup(
          services: [service, systemMetricsMonitor],
          gracefulShutdownSignals: [.sigint],
          cancellationSignals: [.sigterm],
          logger: logger
        )
    
        try await serviceGroup.run()
      }
    }
  4. Propose API changes to Swift System Metrics

    main

    For non-trivial changes that affect the public API, the project uses a lightweight version of the Swift Evolution process. To propose a change, follow these steps:

    1. Create an Issue: Ensure there is a GitHub issue for the feature or change you want to propose.
    2. Create a Proposal Document: Duplicate the SSM-NNNN.md template, replacing NNNN with the next available proposal number.
    3. Fill the Proposal: Link the relevant GitHub issue within your new document and complete the required sections.
    4. Submit for Feedback: Open a pull request containing your proposal to solicit feedback from contributors. Including a link to a prototype implementation branch in the PR description is recommended.
    5. Review Period: Once a maintainer confirms the proposal is ready, a 7-day review period begins. The review ends when a maintainer marks the proposal as Ready for Implementation or Deferred.
    6. Implementation: Ensure an implementation is ready (either in the same PR or a linked separate PR) before merging.
    7. Approval: A proposal is considered Approved once the implementation and proposal PRs are merged and any associated feature flags are enabled unconditionally.
  5. Integrate Swift System Metrics with Service Lifecycle and Grafana

    main

    The ServiceIntegration example demonstrates how to build a complete observability stack using swift-system-metrics. It combines several components to provide a production-ready monitoring setup:

    • SwiftServiceLifecycle: Integrates metric collection into the service lifecycle.
    • SwiftMetrics: Configures the export of collected system metrics.
    • Docker Compose: Orchestrates the application alongside a Grafana container.
    • Grafana Dashboard: Uses a provisioned dashboard to visualize all collected metrics automatically.

    Refer to the ServiceIntegration directory for the full implementation and Docker Compose configuration.

    # See the ServiceIntegration directory for the full implementation
    # It includes SwiftServiceLifecycle, SwiftMetrics, and Docker Compose setup.
  6. Monitor system metrics using SystemMetricsMonitor

    main

    You can monitor process-level system metrics by creating an instance of SystemMetricsMonitor. To ensure the monitor runs alongside your application, it is recommended to integrate it into a ServiceGroup from the ServiceLifecycle package.

    Before running the monitor, ensure you have bootstrapped a MetricsSystem (e.g., using a custom backend or a standard implementation) so that the collected metrics have a destination.

    import SystemMetrics
    import ServiceLifecycle
    import Metrics
    import Logging
    
    @main
    struct Application {
      static func main() async throws {
        let logger = Logger(label: "Application")
        let metrics = MyMetricsBackendImplementation()
        MetricsSystem.bootstrap(metrics)
    
        let service = FooService()
        let systemMetricsMonitor = SystemMetricsMonitor(logger: logger)
    
        let serviceGroup = ServiceGroup(
          services: [service, systemMetricsMonitor],
          gracefulShutdownSignals: [.sigint],
          cancellationSignals: [.sigterm],
          logger: logger
        )
    
        try await serviceGroup.run()
      }
    }
  7. Run the SystemMetricsMonitor using Swift Service Lifecycle

    main

    To ensure the monitor runs as a background service with support for graceful shutdown and UNIX signal handling, integrate it into a ServiceGroup using the Swift Service Lifecycle library.

    Include the SystemMetricsMonitor instance in the services array of your ServiceGroup.

    import SystemMetrics
    import ServiceLifecycle
    import Metrics
    import Logging
    
    @main
    struct Application {
        static func main() async throws {
            let logger = Logger(label: "Application")
            let metrics = MyMetricsBackendImplementation()
            MetricsSystem.bootstrap(metrics)
    
            let service = FooService()
            let systemMetricsMonitor = SystemMetricsMonitor(logger: logger)
    
            let serviceGroup = ServiceGroup(
                services: [service, systemMetricsMonitor],
                gracefulShutdownSignals: [.sigint],
                cancellationSignals: [.sigterm],
                logger: logger
            )
    
            try await serviceGroup.run()
        }
    }
  8. Migrate from swift-metrics-extras to swift-system-metrics

    main

    The package formerly known as swift-metrics-extras has been renamed to swift-system-metrics to better reflect its purpose of collecting and reporting process system metrics.

    API Stability: This rename does not affect the API surface. Existing code using the library's functions and types will continue to work without modification.

    Dependency Update: While GitHub provides redirects for the repository URL, it is recommended to update your Package.swift dependency to point to the new repository URL: apple/swift-system-metrics.

  9. Integrate SystemMetricsMonitor with swift-service-lifecycle

    main

    To run periodic system metrics collection in the background for the duration of your application, use SystemMetricsMonitor. Because it conforms to the Service protocol from swift-service-lifecycle, the recommended pattern is to include it in a ServiceGroup alongside your application services. This ensures the monitor's lifecycle is managed automatically and it shuts down gracefully with your application.

    import SystemMetrics
    import ServiceLifecycle
    import UnixSignals
    import Metrics
    
    @main
    struct Application {
        static func main() async throws {
            let logger = Logger(label: "Application")
            let metrics = MyMetricsBackendImplementation()
            MetricsSystem.bootstrap(metrics)
    
            let service = FooService()
            let systemMetricsMonitor = SystemMetricsMonitor()
            let serviceGroup = ServiceGroup(
                services: [service, systemMetricsMonitor],
                gracefulShutdownSignals: [.sigint],
                cancellationSignals: [.sigterm],
                logger: logger
            )
    
            try await serviceGroup.run()
        }
    }