Skip to content

Commit 60c3a39

Browse files
authored
Merge pull request #370 from cipherstash/cts-logging
Additional logging for ZeroKMS integration
2 parents a7e2a82 + f477695 commit 60c3a39

File tree

9 files changed

+180
-41
lines changed

9 files changed

+180
-41
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ All notable changes to CipherStash Proxy will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [Unreleased]
8+
9+
### Changed
10+
11+
- **Log target renamed**: `KEYSET` log target renamed to `ZEROKMS`. The environment variable `CS_LOG__KEYSET_LEVEL` is now `CS_LOG__ZEROKMS_LEVEL`.
12+
13+
### Removed
14+
15+
- **Log target removed**: `PROXY` log target and `CS_LOG__PROXY_LEVEL` environment variable have been removed.
16+
17+
### Added
18+
19+
- **Cipher cache miss metric**: New Prometheus counter `cipherstash_proxy_keyset_cipher_cache_miss_total` tracks cache misses requiring cipher initialization. This complements the `cipherstash_proxy_keyset_cipher_cache_hit_total` metric, and can be used to calculate cache hit/miss ratio.
20+
- **Cipher init duration metric**: New Prometheus histogram `cipherstash_proxy_keyset_cipher_init_duration_seconds` tracks cipher initialization time including ZeroKMS network calls.
21+
- **Encrypt/decrypt timing**: Debug logs for `encrypt_eql` and `decrypt_eql` now include `duration_ms`.
22+
- **Cache eviction logging**: ScopedCipher cache eviction events are now logged under the `ZEROKMS` target.
23+
- **Slow cipher init warning**: Cipher initialization taking longer than 1 second triggers a warning log.
24+
725
## [2.1.22] - 2026-02-05
826

927
### Added

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@ Set granular log levels by target:
131131
CS_LOG__MAPPER_LEVEL=debug
132132
CS_LOG__AUTHENTICATION_LEVEL=debug
133133
CS_LOG__ENCRYPT_LEVEL=debug
134+
CS_LOG__ZEROKMS_LEVEL=debug
134135
```
135136

136-
Available targets: `DEVELOPMENT`, `AUTHENTICATION`, `CONTEXT`, `ENCRYPT`, `KEYSET`, `PROTOCOL`, `MAPPER`, `SCHEMA`
137+
Available targets: `DEVELOPMENT`, `AUTHENTICATION`, `CONFIG`, `CONTEXT`, `ENCODING`, `ENCRYPT`, `DECRYPT`, `ENCRYPT_CONFIG`, `ZEROKMS`, `MIGRATE`, `PROTOCOL`, `MAPPER`, `SCHEMA`, `SLOW_STATEMENTS`
137138

138139
## Key Development Patterns
139140

DEVELOPMENT.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,12 @@ ENCODING | CS_LOG__ENCODING_LEVEL
244244
ENCRYPT | CS_LOG__ENCRYPT_LEVEL
245245
DECRYPT | CS_LOG__DECRYPT_LEVEL
246246
ENCRYPT_CONFIG | CS_LOG__ENCRYPT_CONFIG_LEVEL
247-
KEYSET | CS_LOG__KEYSET_LEVEL
247+
ZEROKMS | CS_LOG__ZEROKMS_LEVEL
248248
MIGRATE | CS_LOG__MIGRATE_LEVEL
249249
PROTOCOL | CS_LOG__PROTOCOL_LEVEL
250-
PROXY | CS_LOG__PROXY_LEVEL
251250
MAPPER | CS_LOG__MAPPER_LEVEL
252251
SCHEMA | CS_LOG__SCHEMA_LEVEL
252+
SLOW_STATEMENTS | CS_LOG__SLOW_STATEMENTS_LEVEL
253253
```
254254

255255

@@ -587,12 +587,12 @@ ENCODING | CS_LOG__ENCODING_LEVEL
587587
ENCRYPT | CS_LOG__ENCRYPT_LEVEL
588588
DECRYPT | CS_LOG__DECRYPT_LEVEL
589589
ENCRYPT_CONFIG | CS_LOG__ENCRYPT_CONFIG_LEVEL
590-
KEYSET | CS_LOG__KEYSET_LEVEL
590+
ZEROKMS | CS_LOG__ZEROKMS_LEVEL
591591
MIGRATE | CS_LOG__MIGRATE_LEVEL
592592
PROTOCOL | CS_LOG__PROTOCOL_LEVEL
593-
PROXY | CS_LOG__PROXY_LEVEL
594593
MAPPER | CS_LOG__MAPPER_LEVEL
595594
SCHEMA | CS_LOG__SCHEMA_LEVEL
595+
SLOW_STATEMENTS | CS_LOG__SLOW_STATEMENTS_LEVEL
596596
```
597597

598598
### Example

docs/reference/index.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ This page contains reference documentation for configuring CipherStash Proxy and
66

77
- [Proxy config options](#proxy-config-options)
88
- [Recommended settings for development](#recommended-settings-for-development)
9+
- [Per-target log levels](#per-target-log-levels)
910
- [Docker-specific configuration](#docker-specific-configuration)
1011
- [Prometheus metrics](#prometheus-metrics)
1112
- [Available metrics](#available-metrics)
13+
- [Troubleshooting ZeroKMS connections](#troubleshooting-zerokms-connections)
1214
- [Supported architectures](#supported-architectures)
1315

1416

@@ -207,6 +209,27 @@ output = "stdout"
207209
# Env: CS_LOG__ANSI_ENABLED
208210
ansi_enabled = "true"
209211

212+
# Enable slow statement logging
213+
# When enabled, logs detailed timing breakdowns for queries exceeding the threshold
214+
# Optional
215+
# Default: `false`
216+
# Env: CS_LOG__SLOW_STATEMENTS
217+
slow_statements = "false"
218+
219+
# Minimum duration in milliseconds for a statement to be considered slow
220+
# Statements exceeding this threshold are logged with phase-by-phase timing
221+
# Optional
222+
# Default: `2000`
223+
# Env: CS_LOG__SLOW_STATEMENT_MIN_DURATION_MS
224+
slow_statement_min_duration_ms = "2000"
225+
226+
# Minimum duration in milliseconds for a database response to be considered slow
227+
# Tracks per-message read latency from the PostgreSQL server connection
228+
# Optional
229+
# Default: `100`
230+
# Env: CS_LOG__SLOW_DB_RESPONSE_MIN_DURATION_MS
231+
slow_db_response_min_duration_ms = "100"
232+
210233

211234
[prometheus]
212235
# Enable prometheus stats
@@ -241,6 +264,36 @@ CS_DATABASE__CONFIG_RELOAD_INTERVAL="10"
241264
CS_DATABASE__SCHEMA_RELOAD_INTERVAL="10"
242265
```
243266

267+
### Per-target log levels
268+
269+
Proxy supports per-target log level overrides using the pattern `CS_LOG__{TARGET}_LEVEL`. Each target corresponds to an internal component, and can be set independently of the global `CS_LOG__LEVEL`.
270+
271+
Valid values: `error`, `warn`, `info`, `debug`, `trace`. Default for all targets: `info`.
272+
273+
| Environment variable | Target | Description |
274+
|---|---|---|
275+
| `CS_LOG__DEVELOPMENT_LEVEL` | `DEVELOPMENT` | General development logging |
276+
| `CS_LOG__AUTHENTICATION_LEVEL` | `AUTHENTICATION` | Client authentication and SASL |
277+
| `CS_LOG__CONFIG_LEVEL` | `CONFIG` | Configuration loading |
278+
| `CS_LOG__CONTEXT_LEVEL` | `CONTEXT` | Connection context |
279+
| `CS_LOG__ENCODING_LEVEL` | `ENCODING` | Data encoding and decoding |
280+
| `CS_LOG__ENCRYPT_LEVEL` | `ENCRYPT` | Encryption and decryption operations |
281+
| `CS_LOG__DECRYPT_LEVEL` | `DECRYPT` | Decryption operations |
282+
| `CS_LOG__ENCRYPT_CONFIG_LEVEL` | `ENCRYPT_CONFIG` | Encryption configuration loading |
283+
| `CS_LOG__ZEROKMS_LEVEL` | `ZEROKMS` | ZeroKMS cipher init, cache, and connectivity |
284+
| `CS_LOG__MIGRATE_LEVEL` | `MIGRATE` | Schema migration |
285+
| `CS_LOG__PROTOCOL_LEVEL` | `PROTOCOL` | PostgreSQL wire protocol |
286+
| `CS_LOG__MAPPER_LEVEL` | `MAPPER` | SQL statement mapping and transformation |
287+
| `CS_LOG__SCHEMA_LEVEL` | `SCHEMA` | Database schema analysis |
288+
| `CS_LOG__SLOW_STATEMENTS_LEVEL` | `SLOW_STATEMENTS` | Slow statement detection |
289+
290+
Example — enable debug logging for encryption and ZeroKMS:
291+
292+
```bash
293+
CS_LOG__ENCRYPT_LEVEL="debug"
294+
CS_LOG__ZEROKMS_LEVEL="debug"
295+
```
296+
244297
### Docker-specific configuration
245298

246299
As a convenience for local development, if you use [Proxy's Docker container](../proxy.Dockerfile) with its default entrypoint and the below environment variables set, the EQL SQL will be applied to the database, and an example schema (for example, with the `users` table, from the [README Getting Started example](../README.md#getting-started)) will be loaded. These are turned on by default in the [development `docker-compose.yml`](../docker-compose.yml):
@@ -478,7 +531,9 @@ If the proxy is running on a host other than localhost, access on that host.
478531
| Name | Target | Description |
479532
|-----------------------------------------------------------------|-----------|-----------------------------------------------------------------------------|
480533
| `cipherstash_proxy_keyset_cipher_cache_hits_total` | Counter | Number of times a keyset-scoped cipher was found in the cache |
534+
| `cipherstash_proxy_keyset_cipher_cache_miss_total` | Counter | Number of cipher cache misses requiring initialization |
481535
| `cipherstash_proxy_keyset_cipher_init_total` | Counter | Number of times a new keyset-scoped cipher has been initialized |
536+
| `cipherstash_proxy_keyset_cipher_init_duration_seconds` | Histogram | Duration of cipher initialization including ZeroKMS network call |
482537
| `cipherstash_proxy_clients_active_connections` | Gauge | Current number of connections to CipherStash Proxy from clients |
483538
| `cipherstash_proxy_clients_bytes_received_total` | Counter | Number of bytes received by CipherStash Proxy from clients |
484539
| `cipherstash_proxy_clients_bytes_sent_total` | Counter | Number of bytes sent from CipherStash Proxy to clients |
@@ -510,6 +565,68 @@ If the proxy is running on a host other than localhost, access on that host.
510565
| `cipherstash_proxy_statements_total` | Counter | Total number of SQL statements processed by CipherStash Proxy |
511566
| `cipherstash_proxy_statements_unmappable_total` | Counter | Total number of unmappable SQL statements processed by CipherStash Proxy |
512567

568+
## Troubleshooting ZeroKMS connections
569+
570+
### Recommended log settings
571+
572+
For a quick check on ZeroKMS latency and connection issues:
573+
574+
```bash
575+
CS_LOG__ZEROKMS_LEVEL=debug
576+
CS_LOG__ENCRYPT_LEVEL=debug
577+
```
578+
579+
For a deeper investigation, also enable slow statement detection:
580+
581+
```bash
582+
CS_LOG__ZEROKMS_LEVEL=trace
583+
CS_LOG__SLOW_STATEMENTS=true
584+
CS_LOG__SLOW_STATEMENT_MIN_DURATION_MS=500
585+
CS_LOG__SLOW_DB_RESPONSE_MIN_DURATION_MS=50
586+
```
587+
588+
### What to look for in logs
589+
590+
| Signal | Meaning |
591+
|--------|---------|
592+
| `Initializing ZeroKMS ScopedCipher (cache miss)` | Network call to ZeroKMS is about to happen. Frequent occurrences indicate cache churn. |
593+
| `Connected to ZeroKMS` with high `init_duration_ms` | Slow cipher init. Healthy values are <200ms; >1s triggers a warning. |
594+
| `Use cached ScopedCipher` | Cache hit (fast path, no network call). |
595+
| `ScopedCipher evicted from cache` with `cause: Size` | Cache too small for workload. Increase `cipher_cache_size`. |
596+
| `Error initializing ZeroKMS` with high `init_duration_ms` | Network timeout to ZeroKMS. |
597+
| `Error initializing ZeroKMS` with low `init_duration_ms` | Credential or configuration error. |
598+
599+
### Key metrics
600+
601+
Enable Prometheus (`CS_PROMETHEUS__ENABLED=true`) and watch these metrics:
602+
603+
| Metric | Why |
604+
|--------|-----|
605+
| `cipherstash_proxy_keyset_cipher_init_duration_seconds` | Distribution of ZeroKMS init times including network latency. |
606+
| `cipherstash_proxy_keyset_cipher_cache_hits_total` / `cache_miss_total` | Cache hit ratio — should be >95% in steady state. |
607+
| `cipherstash_proxy_statements_session_duration_seconds` minus `execution_duration_seconds` | Encryption overhead per statement (large gap = encryption, not database). |
608+
| `cipherstash_proxy_encryption_error_total` / `decryption_error_total` | Spikes indicate ZeroKMS connectivity issues. |
609+
610+
### Useful PromQL queries
611+
612+
```promql
613+
# P99 cipher init latency
614+
histogram_quantile(0.99, rate(cipherstash_proxy_keyset_cipher_init_duration_seconds_bucket[5m]))
615+
616+
# Cache hit ratio
617+
rate(cipherstash_proxy_keyset_cipher_cache_hits_total[5m])
618+
/ (rate(cipherstash_proxy_keyset_cipher_cache_hits_total[5m])
619+
+ rate(cipherstash_proxy_keyset_cipher_cache_miss_total[5m]))
620+
```
621+
622+
### Quick checklist
623+
624+
1. **Cache hit ratio low?** Tune `CS_SERVER__CIPHER_CACHE_SIZE` (default: 64) and `CS_SERVER__CIPHER_CACHE_TTL_SECONDS` (default: 3600).
625+
2. **`init_duration_ms` >1s?** Network latency to ZeroKMS. Check DNS, firewall rules, and regional proximity.
626+
3. **Large session vs execution duration gap?** Overhead is in encrypt/decrypt, not the database.
627+
4. **Frequent evictions?** Increase `cipher_cache_size` to match your workload's keyset count.
628+
629+
513630
## Supported architectures
514631

515632
CipherStash Proxy is [available as a Docker container image](https://hub.docker.com/r/cipherstash/proxy) for `linux/arm64` architectures.

mise.local.example.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ CS_TLS__PRIVATE_KEY_PATH = "tests/tls/server.key"
4040
CS_LOG__DEVELOPMENT_LEVEL = "info"
4141
CS_LOG__AUTHENTICATION_LEVEL = "info"
4242
CS_LOG__CONTEXT_LEVEL = "info"
43-
CS_LOG__KEYSET_LEVEL = "info"
43+
CS_LOG__ZEROKMS_LEVEL = "info"
4444
CS_LOG__PROTOCOL_LEVEL = "info"
4545
CS_LOG__MAPPER_LEVEL = "debug"
4646
CS_LOG__SCHEMA_LEVEL = "info"

packages/cipherstash-proxy/src/log/mod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tracing_subscriber::{
1616
// All targets are now defined in the targets module using the define_log_targets! macro.
1717
pub use targets::{
1818
AUTHENTICATION, CONFIG, CONTEXT, DECRYPT, DEVELOPMENT, ENCODING, ENCRYPT, ENCRYPT_CONFIG,
19-
KEYSET, MAPPER, MIGRATE, PROTOCOL, PROXY, SCHEMA, SLOW_STATEMENTS,
19+
MAPPER, MIGRATE, PROTOCOL, SCHEMA, SLOW_STATEMENTS, ZEROKMS,
2020
};
2121

2222
static INIT: Once = Once::new();
@@ -50,7 +50,7 @@ mod tests {
5050

5151
use super::*;
5252
use crate::log::targets::{
53-
LogTargetLevels, AUTHENTICATION, CONTEXT, DEVELOPMENT, KEYSET, MAPPER, PROTOCOL, SCHEMA,
53+
LogTargetLevels, AUTHENTICATION, CONTEXT, DEVELOPMENT, MAPPER, PROTOCOL, SCHEMA, ZEROKMS,
5454
};
5555
use crate::test_helpers::MockMakeWriter;
5656
use tracing::dispatcher::set_default;
@@ -123,10 +123,9 @@ mod tests {
123123
encrypt_level: LogLevel::Error,
124124
encrypt_config_level: LogLevel::Error,
125125
decrypt_level: LogLevel::Error,
126-
keyset_level: LogLevel::Trace,
126+
zerokms_level: LogLevel::Trace,
127127
migrate_level: LogLevel::Trace,
128128
protocol_level: LogLevel::Info,
129-
proxy_level: LogLevel::Info,
130129
mapper_level: LogLevel::Info,
131130
schema_level: LogLevel::Info,
132131
config_level: LogLevel::Info,
@@ -162,10 +161,10 @@ mod tests {
162161
assert!(!log_contents.contains("warn/context"));
163162
assert!(log_contents.contains("error/context"));
164163

165-
// with keyset level 'trace', trace should be logged
166-
trace!(target: KEYSET, "trace/keyset");
164+
// with zerokms level 'trace', trace should be logged
165+
trace!(target: ZEROKMS, "trace/zerokms");
167166
let log_contents = make_writer.get_string();
168-
assert!(log_contents.contains("trace/keyset"));
167+
assert!(log_contents.contains("trace/zerokms"));
169168

170169
// with protocol level 'info', info should be logged but not debug
171170
debug!(target: PROTOCOL, "debug/protocol");

packages/cipherstash-proxy/src/log/targets.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ define_log_targets!(
7272
(ENCRYPT, encrypt_level),
7373
(DECRYPT, decrypt_level),
7474
(ENCRYPT_CONFIG, encrypt_config_level),
75-
(KEYSET, keyset_level),
75+
(ZEROKMS, zerokms_level),
7676
(MIGRATE, migrate_level),
7777
(PROTOCOL, protocol_level),
78-
(PROXY, proxy_level),
7978
(MAPPER, mapper_level),
8079
(SCHEMA, schema_level),
8180
(SLOW_STATEMENTS, slow_statements_level),

packages/cipherstash-proxy/src/prometheus.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub const SERVER_BYTES_RECEIVED_TOTAL: &str = "cipherstash_proxy_server_bytes_re
4040

4141
pub const KEYSET_CIPHER_INIT_TOTAL: &str = "cipherstash_proxy_keyset_cipher_init_total";
4242
pub const KEYSET_CIPHER_CACHE_HITS_TOTAL: &str = "cipherstash_proxy_keyset_cipher_cache_hits_total";
43+
pub const KEYSET_CIPHER_CACHE_MISS_TOTAL: &str = "cipherstash_proxy_keyset_cipher_cache_miss_total";
4344
pub const KEYSET_CIPHER_INIT_DURATION_SECONDS: &str =
4445
"cipherstash_proxy_keyset_cipher_init_duration_seconds";
4546

@@ -163,6 +164,10 @@ pub fn start(host: String, port: u16) -> Result<(), Error> {
163164
KEYSET_CIPHER_CACHE_HITS_TOTAL,
164165
"Number of times a keyset-scoped cipher was found in the cache"
165166
);
167+
describe_counter!(
168+
KEYSET_CIPHER_CACHE_MISS_TOTAL,
169+
"Number of times a keyset-scoped cipher was not found in the cache, requiring initialization"
170+
);
166171
describe_histogram!(
167172
KEYSET_CIPHER_INIT_DURATION_SECONDS,
168173
Unit::Seconds,

0 commit comments

Comments
 (0)