# Story 10.30: Serverless Preset

## Status: Complete

## Priority: Medium

## Estimated Effort: Small (1 day)

## Dependencies

- **Depends on:** None
- **Related to:** Story 10.1 (Configuration Presets)

## Epic / Series

Part of Epic 10: Developer Experience

---

## Context / Background

The `production` preset configures both `stdout_json` and `rotating_file` sinks:

```python
"sinks": ["stdout_json", "rotating_file"],
```

This is inappropriate for serverless environments (AWS Lambda, Google Cloud Run, Azure Functions) where:

1. **Ephemeral filesystem** — logs written to disk are lost when the instance terminates
2. **Limited disk space** — serverless containers have constrained storage
3. **Short-lived instances** — file rotation is meaningless for functions that run for seconds
4. **Cloud logging integration** — stdout is automatically captured by CloudWatch, Cloud Logging, etc.

Users deploying to serverless must currently work around this by:
- Using the `fastapi` preset (but it lacks some production hardening)
- Explicitly overriding sinks: `get_logger(preset="production", sinks=["stdout_json"])`
- Using the builder API to customize

A dedicated `serverless` preset would provide production-grade configuration optimized for ephemeral cloud environments.

---

## User Story

**As a** developer deploying to serverless platforms
**I want** a preset optimized for Lambda/Cloud Run/Azure Functions
**So that** I get production-grade logging without inappropriate file sinks

---

## Scope (In / Out)

### In Scope

- New `serverless` preset in `src/fapilog/core/presets.py`
- Production-equivalent security (redaction enabled)
- Stdout-only sink configuration
- Smaller batch size appropriate for short-lived functions
- Documentation updates

### Out of Scope

- Cloud-specific sinks (CloudWatch, Cloud Logging) — use explicit configuration
- Auto-detection of Lambda environment — complementary to Story 10.8
- Lambda/Cloud Run specific enrichers — future story if needed

---

## Acceptance Criteria

### AC1: Serverless Preset Available

**Description:** A `serverless` preset is available via `get_logger(preset="serverless")`.

**Validation:**
```python
from fapilog import get_logger

logger = get_logger(preset="serverless")
logger.info("Running in Lambda")
```

### AC2: Stdout-Only Sink

**Description:** The preset configures only `stdout_json` with no file sinks.

**Validation:**
```python
from fapilog.core.presets import get_preset

config = get_preset("serverless")
assert config["core"]["sinks"] == ["stdout_json"]
assert "rotating_file" not in config["core"]["sinks"]
```

### AC3: Production-Grade Redaction

**Description:** The preset enables the same redactors as `production` (field_mask, regex_mask, url_credentials).

**Validation:**
```python
config = get_preset("serverless")
assert "field_mask" in config["core"]["redactors"]
assert "regex_mask" in config["core"]["redactors"]
assert "url_credentials" in config["core"]["redactors"]
```

### AC4: Optimized Batch Size

**Description:** Batch size is smaller than production to ensure logs flush before function timeout.

**Validation:**
```python
config = get_preset("serverless")
assert config["core"]["batch_max_size"] <= 25  # Smaller than production's 100
```

### AC5: Documentation Updated

**Description:** Preset is documented in user guide with serverless deployment guidance.

---

## Technical Design / Implementation Notes

### Preset Configuration

```python
"serverless": {
    "core": {
        "log_level": "INFO",
        "batch_max_size": 25,  # Smaller batches for short-lived functions
        "drop_on_full": True,  # Don't block in time-constrained environments
        "sinks": ["stdout_json"],  # Stdout only, captured by cloud provider
        "enrichers": ["runtime_info", "context_vars"],
        "redactors": ["field_mask", "regex_mask", "url_credentials"],
    },
    "enricher_config": {
        "runtime_info": {},
        "context_vars": {},
    },
    "redactor_config": {
        "field_mask": {
            "fields_to_mask": [
                "data.password",
                "data.api_key",
                "data.token",
                "data.secret",
                "data.authorization",
                "data.api_secret",
                "data.private_key",
                "data.ssn",
                "data.credit_card",
            ],
        },
        "regex_mask": {
            "patterns": [
                r"(?i).*password.*",
                r"(?i).*passwd.*",
                r"(?i).*api[_-]?key.*",
                r"(?i).*apikey.*",
                r"(?i).*secret.*",
                r"(?i).*token.*",
                r"(?i).*authorization.*",
                r"(?i).*private[_-]?key.*",
                r"(?i).*ssn.*",
                r"(?i).*credit[_-]?card.*",
            ],
        },
        "url_credentials": {},
    },
}
```

### Key Differences from Production

| Setting | Production | Serverless | Rationale |
|---------|------------|------------|-----------|
| `sinks` | `stdout_json`, `rotating_file` | `stdout_json` | No persistent filesystem |
| `batch_max_size` | 100 | 25 | Flush before timeout |
| `drop_on_full` | False | True | Don't block on backpressure |

### Code Locations

- `src/fapilog/core/presets.py` — add preset configuration
- `docs/user-guide/configuration.md` — document new preset
- `tests/unit/test_presets.py` — add preset tests

---

## Tasks

- [ ] Add `serverless` preset to `PRESETS` dict in `presets.py`
- [ ] Add unit tests for preset configuration
- [ ] Update presets documentation
- [ ] Update CHANGELOG.md

---

## Tests

### Unit Tests

- [ ] `test_serverless_preset_exists` — preset is in `list_presets()`
- [ ] `test_serverless_preset_stdout_only` — only stdout_json sink
- [ ] `test_serverless_preset_has_redaction` — all production redactors enabled
- [ ] `test_serverless_preset_batch_size` — batch size <= 25
- [ ] `test_serverless_preset_drop_on_full` — drop_on_full is True

### Integration Tests

- [ ] `test_get_logger_with_serverless_preset` — logger creates successfully

---

## Documentation Updates

- [ ] Update `docs/user-guide/configuration.md` with serverless preset
- [ ] Add serverless deployment section with Lambda/Cloud Run examples
- [ ] Update CHANGELOG.md

---

## Definition of Done

- [x] All acceptance criteria met
- [x] Unit tests passing with >90% coverage
- [x] Documentation updated
- [x] Code review approved

---

## Code Review

**Date:** 2026-01-21
**Reviewer:** Claude
**Verdict:** OK to PR

### Summary

Added `serverless` preset with stdout-only output, optimized batch size (25), `drop_on_full=True`, and production-grade redaction. 8 new tests with strong assertions.

### AC Verification

| Criterion | Evidence |
|-----------|----------|
| AC1: Preset available | `presets.py:126` - "serverless" in PRESETS |
| AC2: Stdout-only sink | `presets.py:131` - `sinks: ["stdout_json"]` |
| AC3: Production redaction | `presets.py:139-168` - full redactor_config matching production |
| AC4: Batch size ≤25 | `presets.py:129` - `batch_max_size: 25` |
| AC5: Docs updated | `configuration.md:15,62,82-87` |

### Quality Gates

- [x] ruff check passed
- [x] ruff format passed
- [x] mypy passed
- [x] Coverage 100%
- [x] No weak assertions
- [x] No dead code

---

## Change Log

| Date       | Change                 | Author |
| ---------- | ---------------------- | ------ |
| 2026-01-21 | Initial story creation | —      |
| 2026-01-21 | Implementation complete, code review passed | —      |
