@owlmetry/cli 0.1.3 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs
CHANGED
|
@@ -7183,7 +7183,7 @@ var switchCommand = new Command("switch").description("Switch active team profil
|
|
|
7183
7183
|
});
|
|
7184
7184
|
|
|
7185
7185
|
// src/index.ts
|
|
7186
|
-
var program2 = new Command().name("owlmetry").version("0.1.
|
|
7186
|
+
var program2 = new Command().name("owlmetry").version("0.1.7").description("OwlMetry CLI \u2014 query metrics and manage your apps from the terminal").addOption(
|
|
7187
7187
|
new Option("--format <format>", "Output format").choices(["table", "json", "log"]).default("table")
|
|
7188
7188
|
).option("--endpoint <url>", "OwlMetry API server URL").option("--api-key <key>", "API key").option("--ingest-endpoint <url>", "OwlMetry ingest endpoint URL (for SDKs; defaults to API endpoint for self-hosted)").option("--team <name-or-id>", "Use a specific team profile for this command");
|
|
7189
7189
|
program2.addCommand(authCommand);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: owlmetry-cli
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.7
|
|
4
4
|
description: >-
|
|
5
5
|
Install the OwlMetry CLI, sign up, and manage projects, apps, metrics,
|
|
6
6
|
funnels, and events. Use when adding OwlMetry to a project, querying
|
|
7
7
|
analytics, or when another OwlMetry skill needs CLI setup as a prerequisite.
|
|
8
|
+
IMPORTANT: You MUST load this skill before running ANY `owlmetry` CLI
|
|
9
|
+
command. The CLI has non-obvious subcommand syntax and flags — do not guess.
|
|
8
10
|
allowed-tools: Bash
|
|
9
11
|
---
|
|
10
12
|
|
|
@@ -14,11 +16,13 @@ OwlMetry is a self-hosted analytics platform for mobile and backend apps. It cap
|
|
|
14
16
|
|
|
15
17
|
The **CLI** is the management and query tool — it does not ingest events. You use it to create projects and apps, define metrics and funnels, query event data, and view analytics. SDKs handle event ingestion using client keys (`owl_client_...`). The CLI uses agent keys (`owl_agent_...`) for reading data and managing resources.
|
|
16
18
|
|
|
19
|
+
> **Do not guess CLI syntax.** The `owlmetry` command has specific subcommand patterns (e.g., `owlmetry apps` not `owlmetry apps list`, flags like `--format json` not `--message`). Always reference the command documentation in this skill file before running a command. If you haven't loaded this skill yet, load it first.
|
|
20
|
+
|
|
17
21
|
## Version Check
|
|
18
22
|
|
|
19
23
|
Run these checks silently. Only inform the user if updates are available.
|
|
20
24
|
|
|
21
|
-
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-cli/SKILL.md | head -5` — compare the `version:` field to `
|
|
25
|
+
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-cli/SKILL.md | head -5` — compare the remote `version:` field to this file's `version:` frontmatter above. If the remote version is newer, ask the user if they want to update the local skill file.
|
|
22
26
|
2. **CLI version** (if installed): compare `owlmetry --version` to `npm view @owlmetry/cli version 2>/dev/null`. If a newer version exists, offer `npm install -g @owlmetry/cli@latest`.
|
|
23
27
|
|
|
24
28
|
If everything is current or the remote is unreachable, continue silently.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: owlmetry-node
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.7
|
|
4
4
|
description: >-
|
|
5
5
|
Integrate the OwlMetry Node.js SDK into a backend service for server-side
|
|
6
6
|
analytics, event tracking, metrics, funnels, and A/B experiments. Use when
|
|
@@ -18,7 +18,7 @@ The SDK has zero runtime dependencies. All calls are non-blocking — events are
|
|
|
18
18
|
|
|
19
19
|
Run these checks silently. Only inform the user if updates are available.
|
|
20
20
|
|
|
21
|
-
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-node/SKILL.md | head -5` — compare the `version:` field to `
|
|
21
|
+
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-node/SKILL.md | head -5` — compare the remote `version:` field to this file's `version:` frontmatter above. If the remote version is newer, ask the user if they want to update.
|
|
22
22
|
2. **SDK version**: `npm ls @owlmetry/node --json 2>/dev/null` for current version, `npm view @owlmetry/node version 2>/dev/null` for latest. If newer, offer `npm install @owlmetry/node@latest`.
|
|
23
23
|
|
|
24
24
|
## Prerequisite
|
|
@@ -27,6 +27,8 @@ You need an **ingest endpoint** and a **client key** (`owl_client_...`) for a ba
|
|
|
27
27
|
|
|
28
28
|
If the user doesn't have these yet, follow the `/owlmetry-cli` skill first — it handles sign-up, project creation, and app creation. The ingest endpoint is saved to `~/.owlmetry/config.json` (`ingest_endpoint` field) and the client key is returned when creating an app.
|
|
29
29
|
|
|
30
|
+
> **Any time you need to run an `owlmetry` CLI command** (querying events, creating metrics/funnels, listing apps, etc.), **load the `/owlmetry-cli` skill first**. Do not guess CLI syntax — it has non-obvious subcommand patterns and flags.
|
|
31
|
+
|
|
30
32
|
## Install
|
|
31
33
|
|
|
32
34
|
```bash
|
|
@@ -37,6 +39,8 @@ Zero runtime dependencies. Node.js 20+. ESM only.
|
|
|
37
39
|
|
|
38
40
|
## Configure
|
|
39
41
|
|
|
42
|
+
Place `Owl.configure()` in the **main entry point** of your service — the root `index.ts`, `server.ts`, or `app.ts` file where your server starts. It must run once at process startup, before any other `Owl` calls. Do **not** put it in a helper/utility file — it should be alongside your other top-level initialization (e.g., `express()`, `Fastify()`, Firebase `setGlobalOptions`).
|
|
43
|
+
|
|
40
44
|
```typescript
|
|
41
45
|
import { Owl } from '@owlmetry/node';
|
|
42
46
|
|
|
@@ -57,6 +61,27 @@ Owl.configure({
|
|
|
57
61
|
- Generates a fresh `sessionId` (UUID) on each `configure()` call
|
|
58
62
|
- Registers a `beforeExit` handler to auto-flush on graceful shutdown
|
|
59
63
|
|
|
64
|
+
**Serverless (Firebase Cloud Functions, AWS Lambda, Vercel):** After adding `Owl.configure()`, also wrap your exported handler functions with `Owl.wrapHandler()` to guarantee events are flushed before the runtime freezes. This is essential boilerplate for serverless — without it, buffered events are lost:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// AWS Lambda / generic serverless:
|
|
68
|
+
export const handler = Owl.wrapHandler(async (event, context) => { ... });
|
|
69
|
+
|
|
70
|
+
// Firebase Cloud Functions v2:
|
|
71
|
+
// IMPORTANT: Explicitly type the request parameter — TypeScript cannot infer
|
|
72
|
+
// the CallableRequest type through wrapHandler's generics.
|
|
73
|
+
import { onCall, type CallableRequest } from 'firebase-functions/v2/https';
|
|
74
|
+
|
|
75
|
+
export const myFunction = onCall(
|
|
76
|
+
Owl.wrapHandler(async (request: CallableRequest) => {
|
|
77
|
+
const { data, auth } = request; // works — TypeScript knows the type
|
|
78
|
+
// ...
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**TypeScript note:** `wrapHandler()` uses generic rest parameters (`<TArgs extends unknown[]>`), which means TypeScript sometimes infers handler parameters as `unknown` when the outer function (like Firebase's `onCall`) expects a specific callback type. If you see type errors like `Property 'data' does not exist on type 'unknown'`, explicitly annotate the handler's parameters (e.g., `request: CallableRequest`, `event: APIGatewayEvent`).
|
|
84
|
+
|
|
60
85
|
## Next Steps — Codebase Instrumentation
|
|
61
86
|
|
|
62
87
|
Once `Owl.configure()` is in place and the project builds successfully, **you MUST stop here and ask the user** which area they'd like to instrument first — even if the user's original prompt asked you to "instrument the app." Do not proceed with any code changes until the user chooses. Present these three options:
|
|
@@ -85,16 +110,21 @@ Events are the core data unit. Use the four log levels to capture different kind
|
|
|
85
110
|
|
|
86
111
|
- **`info`** — normal operations: server started, request handled, job completed, user action processed.
|
|
87
112
|
- **`debug`** — verbose detail for development: cache lookups, query plans, config loading, intermediate state.
|
|
88
|
-
- **`warn`** —
|
|
89
|
-
- **`error`** —
|
|
113
|
+
- **`warn`** — something didn't go as expected but the process can continue: failed validation, precondition checks that fail, slow queries, rate limits approaching, fallback paths, deprecated API usage, missing optional config.
|
|
114
|
+
- **`error`** — a caught exception or hard failure inside a `try`/`catch`: database connection errors, external API timeouts, unhandled rejections, file system errors. Reserve for actual thrown errors, not for anticipated validation outcomes.
|
|
90
115
|
|
|
91
116
|
Choose **message strings** that are specific and searchable. Prefer `"Payment processing failed"` over `"error occurred"`. Use attributes for structured data you'll filter on later.
|
|
92
117
|
|
|
93
118
|
```typescript
|
|
94
119
|
Owl.info('Server started', { port: 4000 });
|
|
95
120
|
Owl.debug('Cache miss', { key: 'user:123' });
|
|
96
|
-
Owl.warn('
|
|
97
|
-
|
|
121
|
+
Owl.warn('Invalid request payload', { field: 'email', reason: 'missing' });
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await db.connect();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
Owl.error('Database connection failed', { host: 'db.example.com', error: String(err) });
|
|
127
|
+
}
|
|
98
128
|
```
|
|
99
129
|
|
|
100
130
|
All methods: `Owl.info/debug/warn/error(message: string, attrs?: Record<string, unknown>)`.
|
|
@@ -106,7 +136,12 @@ Source module (file:line) is auto-captured from the call stack.
|
|
|
106
136
|
Owl.info('Request handled', { method: 'POST', path: '/api/orders', status: 201 });
|
|
107
137
|
Owl.info('Background job completed', { job: 'send-emails', processed: 150 });
|
|
108
138
|
Owl.warn('Rate limit approaching', { current: 95, limit: 100, client_id: 'abc' });
|
|
109
|
-
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
await stripe.charges.create(params);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
Owl.error('Stripe charge failed', { endpoint: '/charges', error: String(err) });
|
|
144
|
+
}
|
|
110
145
|
```
|
|
111
146
|
|
|
112
147
|
## Per-Request User Scoping
|
|
@@ -327,7 +362,8 @@ When instrumenting a backend service, follow this priority:
|
|
|
327
362
|
**Always instrument (events — no CLI setup needed):**
|
|
328
363
|
- Server startup and shutdown (`info`)
|
|
329
364
|
- Request handling: key route hits, responses sent (`info` with method/path/status)
|
|
330
|
-
-
|
|
365
|
+
- Caught exceptions: catch blocks, unhandled rejections, external API failures (`error`)
|
|
366
|
+
- Validation failures and pre-checks: bad input, missing optional config, rate limits (`warn`)
|
|
331
367
|
- Authentication events: login, logout, token refresh (`info`)
|
|
332
368
|
- Core business actions: order placed, payment processed, email sent (`info`)
|
|
333
369
|
- Background jobs: started, completed, failed (`info`/`error`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: owlmetry-swift
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.7
|
|
4
4
|
description: >-
|
|
5
5
|
Integrate the OwlMetry Swift SDK into an iOS or macOS app for analytics,
|
|
6
6
|
event tracking, metrics, funnels, and A/B experiments. Use when
|
|
@@ -18,7 +18,7 @@ The SDK is a static `Owl` enum with no external dependencies. All calls are non-
|
|
|
18
18
|
|
|
19
19
|
Run these checks silently. Only inform the user if updates are available.
|
|
20
20
|
|
|
21
|
-
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-swift/SKILL.md | head -5` — compare the `version:` field to `
|
|
21
|
+
1. **Skill version**: `curl -sf https://raw.githubusercontent.com/Jasonvdb/owlmetry/main/skills/owlmetry-swift/SKILL.md | head -5` — compare the remote `version:` field to this file's `version:` frontmatter above. If the remote version is newer, ask the user if they want to update.
|
|
22
22
|
2. **SDK version**: Read `Package.resolved` for the current resolved revision, then compare against `curl -sf https://api.github.com/repos/Jasonvdb/owlmetry/releases/latest | jq -r .tag_name`. If newer, inform the user.
|
|
23
23
|
|
|
24
24
|
## Prerequisite
|
|
@@ -27,9 +27,16 @@ You need an **ingest endpoint** and a **client key** (`owl_client_...`) for an A
|
|
|
27
27
|
|
|
28
28
|
If the user doesn't have these yet, follow the `/owlmetry-cli` skill first — it handles sign-up, project creation, and app creation. The ingest endpoint is saved to `~/.owlmetry/config.json` (`ingest_endpoint` field) and the client key is returned when creating an app.
|
|
29
29
|
|
|
30
|
+
> **Any time you need to run an `owlmetry` CLI command** (querying events, creating metrics/funnels, listing apps, etc.), **load the `/owlmetry-cli` skill first**. Do not guess CLI syntax — it has non-obvious subcommand patterns and flags.
|
|
31
|
+
|
|
30
32
|
## Add Swift Package
|
|
31
33
|
|
|
32
|
-
**
|
|
34
|
+
**Minimum platforms:** iOS 16.0, macOS 13.0. Zero external dependencies.
|
|
35
|
+
|
|
36
|
+
### Option A — Package.swift projects
|
|
37
|
+
|
|
38
|
+
If the project has a `Package.swift`, add the dependency there:
|
|
39
|
+
|
|
33
40
|
```swift
|
|
34
41
|
dependencies: [
|
|
35
42
|
.package(url: "https://github.com/Jasonvdb/owlmetry.git", branch: "main")
|
|
@@ -42,33 +49,35 @@ Add to your target:
|
|
|
42
49
|
])
|
|
43
50
|
```
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
Then run `swift package resolve` to fetch the dependency.
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
### Option B — Xcode projects (.xcodeproj)
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
For `.xcodeproj`-based projects with no `Package.swift`, add the OwlMetry Swift package by editing `<Project>.xcodeproj/project.pbxproj` directly to add a remote Swift package reference for `https://github.com/Jasonvdb/owlmetry.git` (branch: `main`, product: `OwlMetry`). Do not ask the user to add it manually in Xcode.
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
### Option C — Ask the user (last resort)
|
|
52
59
|
|
|
53
|
-
If
|
|
60
|
+
If pbxproj editing fails or the project structure is too complex, ask the user to add the package in Xcode:
|
|
54
61
|
|
|
55
|
-
1.
|
|
56
|
-
2.
|
|
57
|
-
3.
|
|
58
|
-
4.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
1. File > Add Package Dependencies
|
|
63
|
+
2. Enter URL: `https://github.com/Jasonvdb/owlmetry.git`
|
|
64
|
+
3. Set rule to **Branch** > `main`
|
|
65
|
+
4. Add **OwlMetry** to the app target
|
|
66
|
+
|
|
67
|
+
## Verify Package Integration
|
|
68
|
+
|
|
69
|
+
After adding the package, resolve dependencies and build:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
xcodebuild -resolvePackageDependencies -project <path>.xcodeproj -quiet
|
|
73
|
+
xcodebuild -project <path>.xcodeproj -scheme <SchemeName> -destination 'platform=iOS Simulator,name=iPhone 16' build -quiet
|
|
74
|
+
```
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
If the build succeeds, proceed with configuration. The "No such module 'OwlMetry'" warning in editors (SourceKit) is expected and resolves during a real `xcodebuild`.
|
|
68
77
|
|
|
69
78
|
## Configure
|
|
70
79
|
|
|
71
|
-
Configuration must happen once,
|
|
80
|
+
Configuration must happen once, as early as possible — in the `@main` App `init()` or AppDelegate `didFinishLaunching`. **Do not defer it** to a later point (e.g., after async setup or user consent). The SDK measures app launch time (`_launch_ms`) from process start to the `configure()` call, so placing it early gives an accurate cold-start metric. It also ensures no events are dropped before configuration. Each `configure()` call generates a fresh `session_id` (UUID) that groups all subsequent events together.
|
|
72
81
|
|
|
73
82
|
```swift
|
|
74
83
|
import OwlMetry
|
|
@@ -94,35 +103,119 @@ struct MyApp: App {
|
|
|
94
103
|
- `apiKey: String` — client key, must start with `owl_client_` (required)
|
|
95
104
|
- `flushOnBackground: Bool` — auto-flush when app backgrounds (default: `true`)
|
|
96
105
|
- `compressionEnabled: Bool` — gzip request bodies (default: `true`)
|
|
106
|
+
- `networkTrackingEnabled: Bool` — auto-track URLSession HTTP requests (default: `true`)
|
|
97
107
|
|
|
98
108
|
Auto-detects: bundle ID, debug mode (`#if DEBUG`). Auto-generates: session ID (fresh each launch).
|
|
99
109
|
|
|
110
|
+
## User Identity (set up during initial configuration)
|
|
111
|
+
|
|
112
|
+
After adding `Owl.configure()`, find where the app handles authentication and add `Owl.setUser()` / `Owl.clearUser()`. This is part of the basic setup — do it now, before moving on to instrumentation.
|
|
113
|
+
|
|
114
|
+
Look for the auth state change handler (e.g., Firebase Auth listener, login/logout methods) and add:
|
|
115
|
+
|
|
116
|
+
```swift
|
|
117
|
+
// After successful login — claims all previous anonymous events for this user
|
|
118
|
+
Owl.setUser(userId)
|
|
119
|
+
|
|
120
|
+
// On logout — reverts to anonymous tracking
|
|
121
|
+
Owl.clearUser()
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Where to find it:** Search for login/logout methods, auth state listeners, or session management code. Look for patterns like setting a user ID on other services (crash reporting, analytics), storing auth tokens, or clearing user state. Place `Owl.setUser()` right after the user ID becomes available. Place `Owl.clearUser()` in the sign-out/logout handler.
|
|
125
|
+
|
|
126
|
+
The SDK automatically flushes buffered events before claiming identity, so anonymous events from before login are retroactively linked to the user.
|
|
127
|
+
|
|
100
128
|
## Next Steps — Codebase Instrumentation
|
|
101
129
|
|
|
102
|
-
Once `Owl.configure()` is in place and the project builds successfully, **you MUST stop here and ask the user** which area they'd like to instrument first — even if the user's original prompt asked you to "instrument the app." Do not proceed with any code changes until the user chooses. Present these
|
|
130
|
+
Once `Owl.configure()` is in place and the project builds successfully, **you MUST stop here and ask the user** which area they'd like to instrument first — even if the user's original prompt asked you to "instrument the app." Do not proceed with any code changes until the user chooses. Present these options:
|
|
103
131
|
|
|
104
|
-
1. **
|
|
105
|
-
2. **
|
|
106
|
-
3. **
|
|
132
|
+
1. **Screen tracking** — Add `.owlScreen("ScreenName")` to every distinct screen in the app. This is the quickest win — automatic screen view and time-on-screen tracking with a single modifier per screen. No CLI setup needed.
|
|
133
|
+
2. **Event & error logging** — Audit the codebase for user actions, error handling, and key flows. Add `Owl.info()`, `Owl.warn()`, `Owl.error()` calls at meaningful points. This is SDK-only — no CLI setup required beyond what's already done.
|
|
134
|
+
3. **Structured metrics** — Identify operations worth measuring (data loading, image processing, etc.). Add `Owl.startOperation()` / `Owl.recordMetric()` to track durations and success rates. **Requires CLI first:** each metric slug must be defined on the server via `owlmetry metrics create` (use the `/owlmetry-cli` skill) before the SDK can emit events for it.
|
|
135
|
+
4. **Funnel tracking** — Identify user journeys (onboarding, checkout, key conversions). Add `Owl.track()` calls at each step to measure drop-off. **Requires CLI first:** the funnel definition (with steps and event filters) must be created via `owlmetry funnels create` (use the `/owlmetry-cli` skill) before tracking makes sense.
|
|
107
136
|
|
|
108
137
|
After the user chooses, do a thorough audit of the entire codebase to find all relevant locations, then present a summary of proposed changes before making any edits.
|
|
109
138
|
|
|
139
|
+
## Screen Tracking (`.owlScreen()`)
|
|
140
|
+
|
|
141
|
+
The SDK provides a SwiftUI view modifier that automatically tracks screen appearances and time-on-screen with zero manual event calls.
|
|
142
|
+
|
|
143
|
+
```swift
|
|
144
|
+
struct HomeView: View {
|
|
145
|
+
var body: some View {
|
|
146
|
+
VStack { ... }
|
|
147
|
+
.owlScreen("Home")
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
struct SettingsView: View {
|
|
152
|
+
var body: some View {
|
|
153
|
+
Form { ... }
|
|
154
|
+
.owlScreen("Settings")
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**What it does automatically:**
|
|
160
|
+
- On appear: emits `sdk:screen_appeared` (info level) with `screenName` set — included in production data
|
|
161
|
+
- On disappear: emits `sdk:screen_disappeared` (debug level) with `screenName` set and `_duration_ms` attribute — only visible in dev data mode
|
|
162
|
+
|
|
163
|
+
**Where to place it:** Attach `.owlScreen("ScreenName")` to the outermost view of each screen — typically on the `NavigationStack`, `Form`, `ScrollView`, or root `VStack`. Use it on every distinct screen in the app. Choose names that are short, readable, and consistent (e.g., `"Home"`, `"Settings"`, `"Profile"`, `"Checkout"`).
|
|
164
|
+
|
|
165
|
+
**Prefer `.owlScreen()` over manual `Owl.info()` for screen views** — it handles both appear and disappear with duration tracking. Use manual `Owl.info()` with `screenName:` only for events within a screen (button taps, state changes), not for screen appearances themselves.
|
|
166
|
+
|
|
167
|
+
## Network Request Tracking
|
|
168
|
+
|
|
169
|
+
The SDK automatically tracks all URLSession HTTP requests made via completion handler APIs. This is **enabled by default** — no code needed beyond `Owl.configure()`. To disable:
|
|
170
|
+
|
|
171
|
+
```swift
|
|
172
|
+
try Owl.configure(
|
|
173
|
+
endpoint: "https://ingest.owlmetry.com",
|
|
174
|
+
apiKey: "owl_client_...",
|
|
175
|
+
networkTrackingEnabled: false
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**What it captures automatically:**
|
|
180
|
+
- `_http_method` — GET, POST, etc.
|
|
181
|
+
- `_http_url` — sanitized URL (scheme + host + path only, query params stripped for privacy)
|
|
182
|
+
- `_http_status` — response status code
|
|
183
|
+
- `_http_duration_ms` — request duration in milliseconds
|
|
184
|
+
- `_http_response_size` — response body size in bytes
|
|
185
|
+
- `_http_error` — error description (failures only)
|
|
186
|
+
|
|
187
|
+
**Log levels:** `.info` for 2xx/3xx responses, `.warn` for 4xx/5xx, `.error` for network failures (no response).
|
|
188
|
+
|
|
189
|
+
**Safety:** The SDK's own requests to the OwlMetry ingest endpoint are automatically filtered out. Query parameters are stripped from URLs to prevent accidental logging of tokens or user IDs.
|
|
190
|
+
|
|
191
|
+
**Coverage:** Tracks requests made with `URLSession.dataTask(with:completionHandler:)` (both URL and URLRequest overloads). Delegate-based and async/await requests are not tracked in this version.
|
|
192
|
+
|
|
110
193
|
## Log Events
|
|
111
194
|
|
|
112
195
|
Events are the core unit of data in OwlMetry. Use the four log levels to capture different kinds of information:
|
|
113
196
|
|
|
114
197
|
- **`info`** — normal operations worth recording: screen views, user actions, feature usage, successful completions. This is your default level.
|
|
115
198
|
- **`debug`** — verbose detail useful only during development: cache hits, state transitions, intermediate values. These are filtered out in production data mode.
|
|
116
|
-
- **`warn`** — something
|
|
117
|
-
- **`error`** —
|
|
199
|
+
- **`warn`** — something didn't go as expected but the app can continue: failed validation, precondition checks that fail, slow responses, fallback paths taken, deprecated API usage, missing optional data.
|
|
200
|
+
- **`error`** — a caught exception or hard failure inside a `do`/`catch` block: network errors, JSON decode failures, file I/O errors, keychain access failures. Reserve for actual thrown errors, not for anticipated validation outcomes.
|
|
118
201
|
|
|
119
202
|
Choose **message strings** that are specific and searchable. Prefer `"Failed to load profile image"` over `"error"`. Use `screenName` to tie events to where they happened in the UI. Use `customAttributes` for structured data you'll want to filter or search on later.
|
|
120
203
|
|
|
121
204
|
```swift
|
|
205
|
+
// In a screen context — pass screenName to tie the event to the screen
|
|
122
206
|
Owl.info("User opened settings", screenName: "SettingsView")
|
|
123
207
|
Owl.debug("Cache hit", screenName: "HomeView", customAttributes: ["key": "user_prefs"])
|
|
124
|
-
Owl.warn("
|
|
125
|
-
|
|
208
|
+
Owl.warn("Invalid email format", screenName: "SignUpView", customAttributes: ["input": email])
|
|
209
|
+
|
|
210
|
+
do {
|
|
211
|
+
let profile = try await api.loadProfile(id: userId)
|
|
212
|
+
} catch {
|
|
213
|
+
Owl.error("Failed to load profile", screenName: "ProfileView", customAttributes: ["error": "\(error)"])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Outside a screen context — omit screenName entirely
|
|
217
|
+
Owl.info("Background sync completed", customAttributes: ["items": "\(count)"])
|
|
218
|
+
Owl.error("Keychain write failed", customAttributes: ["error": "\(error)"])
|
|
126
219
|
```
|
|
127
220
|
|
|
128
221
|
All logging methods share the same signature:
|
|
@@ -130,6 +223,8 @@ All logging methods share the same signature:
|
|
|
130
223
|
Owl.info(_ message: String, screenName: String? = nil, customAttributes: [String: String]? = nil)
|
|
131
224
|
```
|
|
132
225
|
|
|
226
|
+
**`screenName` is optional.** Only pass it when the event originates from a specific screen in the UI (e.g., a button tap handler inside a view). **Do NOT pass `screenName`** when logging from utility functions, services, managers, network layers, background tasks, or anywhere that isn't directly tied to a visible screen. Passing a fabricated or guessed screen name is worse than omitting it — it pollutes screen-level analytics.
|
|
227
|
+
|
|
133
228
|
Source file, function, and line are auto-captured.
|
|
134
229
|
|
|
135
230
|
**Avoid logging PII** (emails, phone numbers, passwords) or high-frequency events (every frame, every scroll position). Focus on actions and outcomes.
|
|
@@ -251,10 +346,11 @@ Owl.clearExperiments()
|
|
|
251
346
|
When instrumenting a new app, follow this priority:
|
|
252
347
|
|
|
253
348
|
**Always instrument (events — no CLI setup needed):**
|
|
349
|
+
- Screen views (`.owlScreen("ScreenName")` on every distinct screen)
|
|
254
350
|
- App launch / cold start (`info` in `init()` or `didFinishLaunching`)
|
|
255
|
-
- Key screen views (`info` with `screenName` in `onAppear`)
|
|
256
351
|
- Authentication events (login, logout, signup)
|
|
257
|
-
-
|
|
352
|
+
- Caught exceptions (`error` in `catch` blocks, error handlers)
|
|
353
|
+
- Validation failures and pre-checks (`warn` for bad input, missing optional data, fallback paths)
|
|
258
354
|
- Core business actions (purchase, share, create, delete)
|
|
259
355
|
|
|
260
356
|
**Instrument when relevant (metrics — requires CLI `owlmetry metrics create` first):**
|
|
@@ -266,9 +362,10 @@ When instrumenting a new app, follow this priority:
|
|
|
266
362
|
- A/B experiments when testing alternative UI or flows
|
|
267
363
|
|
|
268
364
|
**Where to place calls:**
|
|
269
|
-
- Screen views: `.
|
|
270
|
-
- User actions: button action handlers, gesture callbacks
|
|
271
|
-
- Errors: `catch` blocks, `Result.failure` handlers
|
|
365
|
+
- Screen views: `.owlScreen("Name")` on the outermost view of each screen (SwiftUI), `viewDidAppear` in UIKit
|
|
366
|
+
- User actions: button action handlers, gesture callbacks — pass `screenName` since you know which screen the user is on
|
|
367
|
+
- Errors: `catch` blocks, `Result.failure` handlers — pass `screenName` only if the error is caught inside a view; omit it if caught in a service, manager, or utility
|
|
368
|
+
- Services, utilities, background tasks: log freely but **never pass `screenName`** — these are not screen-bound
|
|
272
369
|
- Metrics: wrap the async operation between `startOperation()` and `complete()`/`fail()`
|
|
273
370
|
|
|
274
371
|
**What NOT to instrument:**
|
|
@@ -296,3 +393,10 @@ Every event automatically includes:
|
|
|
296
393
|
- `_connection` — network type (wifi, cellular, ethernet, offline) via `NWPathMonitor`
|
|
297
394
|
- `experiments` — current A/B experiment assignments
|
|
298
395
|
- `environment` — specific runtime (ios, ipados, macos)
|
|
396
|
+
|
|
397
|
+
**Auto-emitted lifecycle events** (no manual calls needed):
|
|
398
|
+
- `sdk:session_started` — on `configure()`, includes `_launch_ms` (time from process start to configure)
|
|
399
|
+
- `sdk:app_foregrounded` — when app enters foreground
|
|
400
|
+
- `sdk:app_backgrounded` — when app enters background
|
|
401
|
+
- `sdk:screen_appeared` (info) / `sdk:screen_disappeared` (debug) — when using `.owlScreen()` modifier (disappear includes `_duration_ms`)
|
|
402
|
+
- `sdk:network_request` (info/warn/error) — URLSession HTTP requests with method, URL, status, duration (enabled by default, disable with `networkTrackingEnabled: false`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmetry/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "OwlMetry CLI — manage projects, apps, metrics, funnels, and events from the terminal. Includes AI skill files for agent-assisted development.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|