Skip to content
Merged
8 changes: 4 additions & 4 deletions Examples/ServiceIntegration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ let systemMetricsMonitor = SystemMetricsMonitor(

This approach decouples the monitor from global state and allows you to use different metrics backends for different components.

## Running the Example
## Running the example

From the `swift-system-metrics` package root run:
From the `swift-system-metrics` package root, run:

```bash
docker-compose -f Examples/ServiceIntegration/docker-compose.yaml up --build
```

This will build and run 2 containers: `grafana` and `systemmetricsmonitor`.
The command builds and runs 2 containers: `grafana` and `systemmetricsmonitor`.

## Grafana Dashboard

Expand All @@ -42,6 +42,6 @@ The dashboard provides four visualizations that map to the metrics collected by

1. CPU Usage % calculated as `rate(process_cpu_seconds_total)`.

1. Residential Memory (`process_resident_memory_bytes`) and Virtual Memory (`process_virtual_memory_bytes`) consumption.
1. Resident Memory (`process_resident_memory_bytes`) and Virtual Memory (`process_virtual_memory_bytes`) consumption.

1. Open File Descriptors (`process_open_fds`) and Max File Descriptors (`process_max_fds`)
63 changes: 36 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Automatically collects process-level system metrics (memory, CPU, file descriptors) and reports them through the [SwiftMetrics](https://github.com/apple/swift-metrics) API.

## Collected Metrics
## Collected metrics

The following metrics are collected and reported as gauges:
The monitor collects and reports the following metrics as gauges:

- **Virtual Memory** (`process_virtual_memory_bytes`) - Virtual memory size in bytes
- **Resident Memory** (`process_resident_memory_bytes`) - Resident Set Size (RSS) in bytes
Expand All @@ -16,25 +16,34 @@ The following metrics are collected and reported as gauges:
## Quick start

```swift
import Logging
import SystemMetrics
import ServiceLifecycle
import Metrics
import Logging

// Create a logger, or use one of the existing loggers
let logger = Logger(label: "MyLogger")

// Create the monitor
let monitor = SystemMetricsMonitor(logger: logger)

// Create the service
let serviceGroup = ServiceGroup(
services: [monitor],
gracefulShutdownSignals: [.sigint],
cancellationSignals: [.sigterm],
logger: logger
)

// Start collecting metrics
try await serviceGroup.run()
@main
struct Application {
static func main() async throws {
// Create a logger, or use one of the existing loggers
let logger = Logger(label: "Application")
let metrics = MyMetricsBackendImplementation()
MetricsSystem.bootstrap(metrics)

let service = FooService()
// Create the monitor
let systemMetricsMonitor = SystemMetricsMonitor(logger: logger)

// Create the service
let serviceGroup = ServiceGroup(
services: [service, systemMetricsMonitor],
gracefulShutdownSignals: [.sigint],
cancellationSignals: [.sigterm],
logger: logger
)

try await serviceGroup.run()
}
}
```

See the [`SystemMetrics` documentation](https://swiftpackageindex.com/apple/swift-system-metrics/documentation/systemmetrics) for details.
Expand All @@ -45,24 +54,24 @@ Add Swift System Metrics as a dependency in your `Package.swift`:

```swift
dependencies: [
.package(url: "https://github.com/apple/swift-system-metrics.git", from: "1.0.0")
.package(url: "https://github.com/apple/swift-system-metrics.git", from: "1.0.0")
]
```

Then add ``SystemMetrics`` to your target:
Then add `SystemMetrics` to your target:

```swift
.target(
name: "YourTarget",
dependencies: [
.product(name: "SystemMetrics", package: "swift-system-metrics")
]
name: "YourTarget",
dependencies: [
.product(name: "SystemMetrics", package: "swift-system-metrics")
]
)
```

## Example & Grafana Dashboard
## Example and Grafana dashboard

A complete working example with a pre-built Grafana dashboard is available in [Examples/ServiceIntegration](Examples/ServiceIntegration). The example includes:
[Examples/ServiceIntegration](Examples/ServiceIntegration) provides a complete working example with a pre-built Grafana dashboard. The example includes:

- `SwiftServiceLifecycle` integration.
- `SwiftMetrics` configured to export the metrics.
Expand Down
75 changes: 75 additions & 0 deletions Sources/SystemMetrics/Docs.docc/GettingStarted.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Capturing and reporting process metrics

Use the system metrics monitor in your application to provide metrics from the process where you run your service.

### Add the project dependency

Add `swift-system-metrics` as a dependency to your app and executable target:

```bash
swift package add-dependency https://github.com/apple/swift-system-metrics --from 1.0.0
```

```bash
swift package add-target-dependency SystemMetrics MyExecutableTarget --package swift-system-metrics
```

### Create a system monitor service

Import the `SystemMetrics` module, then create and add a ``SystemMetricsMonitor`` to a service group.

```swift
import SystemMetrics
// Import and create a logger, or use one of the existing loggers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Import and create a logger, or use one of the existing loggers

import Logging

let logger = Logger(label: "MyService")

// Create the monitor
let systemMetricsMonitor = SystemMetricsMonitor(logger: logger)
```

The monitor collects and reports metrics periodically using the global `MetricsSystem` that Swift Metrics provides.
You can configure the polling interval with your own ``SystemMetricsMonitor/Configuration``, as well as the `MetricsSystem`, when you create the monitor:

```swift
let systemMetricsMonitor = SystemMetricsMonitor(
configuration: .init(pollInterval: .seconds(5)),
logger: logger
Comment on lines +37 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
configuration: .init(pollInterval: .seconds(5)),
logger: logger
configuration: .init(pollInterval: .seconds(5)),
metricsFactory: myMetricsFactory,
logger: logger

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to promote the initializer with the custom factory as a baseline expectation? I got the impression that the one without would be more commonly used/desired here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested the change because the text above it mentions the factory. It was strange to mention it without showing the code.

But we can also remove it and also remove the mention in the text, either is fine.

)
```

### Run the service in your app

Use [Swift Service Lifecycle](https://github.com/swift-server/swift-service-lifecycle) to run the monitor as a background service with support for graceful shutdown and UNIX signal handling.
To do so, include the system metrics monitor you created in a service group and run the group in your application.

The following code bootstraps your own metrics backend, creates a system metrics monitor, and uses service lifecycle to run both:

```swift
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()
}
}
```
13 changes: 7 additions & 6 deletions Sources/SystemMetrics/Docs.docc/Proposals/Proposals.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ Collaborate on API changes to Swift System Metrics by writing a proposal.

For non-trivial changes that affect the public API, the Swift System Metrics project adopts a lightweight version of the [Swift Evolution](https://github.com/apple/swift-evolution/blob/main/process.md) process.

Writing a proposal first helps discuss multiple possible solutions early, apply useful feedback from other contributors, and avoid reimplementing the same feature multiple times.
Writing a proposal first helps you discuss multiple possible solutions early, apply useful feedback from other contributors, and avoid reimplementing the same feature multiple times.

While it's encouraged to get feedback by opening a pull request with a proposal early in the process, it's also important to consider the complexity of the implementation when evaluating different solutions. For example, this might mean including a link to a branch containing a prototype implementation of the feature in the pull request description.
Get feedback early by opening a pull request with your proposal, but also consider the complexity of the implementation when evaluating different solutions. For example, include a link to a branch containing a prototype implementation of the feature in the pull request description.

> Note: The goal of this process is to help solicit feedback from the whole community around the project, and we will continue to refine the proposal process itself. Use your best judgement, and don't hesitate to propose changes to the proposal structure itself!
> Note: The goal of this process is to solicit feedback from the whole community around the project, and the project continues to refine the proposal process itself. Use your best judgment, and don't hesitate to propose changes to the proposal structure itself.

### Steps

1. Make sure there's a GitHub issue for the feature or change you would like to propose.
2. Duplicate the `SSM-NNNN.md` document and replace `NNNN` with the next available proposal number.
3. Link the GitHub issue from your proposal, and fill in the proposal.
4. Open a pull request with your proposal and solicit feedback from other contributors.
5. Once a maintainer confirms that the proposal is ready for review, the state is updated accordingly. The review period is 7 days, and ends when one of the maintainers marks the proposal as Ready for Implementation, or Deferred.
6. Before the pull request is merged, there should be an implementation ready, either in the same pull request, or a separate one, linked from the proposal.
7. The proposal is considered Approved once the implementation, proposal PRs have been merged, and, if originally disabled by a feature flag, feature flag enabled unconditionally.
5. Once a maintainer confirms that the proposal is ready for review, we update the state accordingly. The review period is 7 days, and ends when one of the maintainers marks the proposal as Ready for Implementation, or Deferred.
6. Before merging the pull request, ensure an implementation is ready, either in the same pull request or in a separate one linked from the proposal.
7. A proposal becomes Approved once you merge the implementation and proposal PRs, and enable any feature flags unconditionally.

If you have any questions, ask in an issue on GitHub.

Expand All @@ -37,3 +37,4 @@ If you have any questions, ask in an issue on GitHub.

- <doc:SSM-NNNN>
- <doc:SSM-0001>
- <doc:SSM-0002>
12 changes: 6 additions & 6 deletions Sources/SystemMetrics/Docs.docc/Proposals/SSM-NNNN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ A short, one-sentence overview of the feature or change.

### Motivation

Describe the problems that this proposal aims to address, and what workarounds adopters have to employ currently, if any.
Describe the problems that this proposal addresses, and what workarounds adopters currently use, if any.

### Proposed solution

Describe your solution to the problem. Provide examples and describe how they work. Show how your solution is better than current workarounds.

This section should focus on what will change for the _adopters_ of Swift System Metrics.
Focus this section on what will change for the _adopters_ of Swift System Metrics.

### Detailed design

Describe the implementation of the feature, a link to a prototype implementation is encouraged here.
Describe the implementation of the feature. Include a link to a prototype implementation.

This section should focus on what will change for the _contributors_ to Swift System Metrics.
Focus this section on what will change for the _contributors_ to Swift System Metrics.

### API stability

Discuss the API implications, making sure to considering all of:
Discuss the API implications, making sure to consider all of:
- existing metrics data provider implementations
- existing users of `SystemMetrics`

Expand All @@ -46,4 +46,4 @@ Discuss any potential future improvements to the feature.

### Alternatives considered

Discuss the alternative solutions considered, even during the review process itself.
Discuss the alternative solutions you considered, even during the review process itself.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# ``SystemMetricsMonitor/Configuration``

## Topics

### Creating a monitor configuration

- ``init(pollInterval:)``
- ``default``

### Inspecting the configuration

- ``interval``
13 changes: 13 additions & 0 deletions Sources/SystemMetrics/Docs.docc/Reference/SystemMetricsMonitor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ``SystemMetricsMonitor``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been meaning to ask - what's the docc command to generate these? Or did you hand-write them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol - I hand wrote these. There's not a DocC command to generate them one off, unfortunately- I've just got 'my pattern' of how I do it, which it mostly stub, see what show's up in preview, then iterate to drop the right pieces into place.

That said, there is a curation generation command - although I'm not certain if anyone actually uses it, as it's not detailed anywhere in the docs. The gist of which is:

  • create/get the symbols for the target/module
  • use docc process-catalog to emit a default catalog
mkdir .symbolgraphs
xcrun swift build --target MyProjectExample \
  -Xswiftc -emit-symbol-graph \
  -Xswiftc -emit-symbol-graph-dir \
  -Xswiftc .symbolgraphs
mkdir Sources/MyProjectExample/Documentation.docc

# better generation using --from-symbol
# which constrains the generated content to MyProjectExample
xcrun docc process-catalog \
  emit-generated-curation \
  Sources/MyProjectExample/Documentation.docc \
  --from-symbol MyProjectExample \
  --additional-symbol-graph-dir .symbolgraphs

So it's kind of a pain to use...


## Topics

### Creating a system metrics monitor

- ``init(configuration:logger:)``
- ``init(configuration:metricsFactory:logger:)``
- ``Configuration``

### Running a monitor

- ``run()``
Loading