Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ That is all that is required to run `pytest`.

`devservices serve` starts the development server.

When the devserver is running, its full console output (all honcho-managed processes — `server`, `taskworker`, kafka consumers, webpack/watchers, etc.) is teed to `.artifacts/dev.log`, ANSI-stripped and gitignored. Agents can't see the devserver terminal, so `tail`/`grep` this file to inspect what's happening (startup, reloads, request logs, tracebacks). The file is truncated on each devserver process start (in-process granian reloads keep appending); override its path with `SENTRY_DEV_LOG_FILE`. Dev-only — this teeing lives in `sentry devserver` and is not used in production.

#### Linting

prek is the single entrypoint for all lint, format, and type-checking tools.
Expand Down
13 changes: 11 additions & 2 deletions src/sentry/runner/commands/devserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import threading
from collections.abc import MutableSequence, Sequence
from pathlib import Path
from typing import NoReturn

import click
Expand Down Expand Up @@ -461,9 +462,17 @@ def devserver(

cwd = os.path.realpath(os.path.join(settings.PROJECT_ROOT, os.pardir, os.pardir))

from sentry.runner.formatting import get_honcho_printer
from sentry.runner.formatting import TeeStream, get_honcho_printer

honcho_printer = get_honcho_printer(prefix=prefix, pretty=pretty)
log_path = Path(
os.environ.get("SENTRY_DEV_LOG_FILE", os.path.join(cwd, ".artifacts", "dev.log"))
)
log_path.parent.mkdir(parents=True, exist_ok=True)
log_file = open(log_path, "w", encoding="utf-8")

honcho_printer = get_honcho_printer(
prefix=prefix, pretty=pretty, output=TeeStream(sys.stdout, log_file)
)

manager = Manager(honcho_printer)
for name, cmd in daemons:
Expand Down
36 changes: 33 additions & 3 deletions src/sentry/runner/formatting.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
from __future__ import annotations

import re
from typing import TYPE_CHECKING
import sys
from typing import IO, TYPE_CHECKING, cast

if TYPE_CHECKING:
import honcho.printer

_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")


class TeeStream:
"""Mirror writes to a console stream verbatim and a log file with ANSI stripped."""

def __init__(self, console: IO[str], log_file: IO[str]) -> None:
self._console = console
self._log_file = log_file

def write(self, s: str) -> int:
self._console.write(s)
self._log_file.write(_ANSI_RE.sub("", s))
self._log_file.flush()
return len(s)

def flush(self) -> None:
self._console.flush()
self._log_file.flush()

def isatty(self) -> bool:
# Delegate so honcho keeps coloring the console; the file copy is stripped above.
return self._console.isatty()


# Sentry colors taken from our design system. Might not look good on all
# terminal themes tbh
COLORS = {
Expand Down Expand Up @@ -75,7 +101,9 @@ def colorize_traceback(pattern: re.Match[str]) -> str:
)


def get_honcho_printer(*, prefix: bool, pretty: bool) -> honcho.printer.Printer:
def get_honcho_printer(
*, prefix: bool, pretty: bool, output: IO[str] | TeeStream | None = None
) -> honcho.printer.Printer:
import honcho.printer

class SentryPrinter(honcho.printer.Printer):
Expand Down Expand Up @@ -117,4 +145,6 @@ def write(self, message: honcho.printer.Message) -> None:
for line in string.splitlines():
self.output.write(f"{prefix}{line}\n")

return SentryPrinter(prefix=prefix)
return SentryPrinter(
prefix=prefix, output=cast("IO[str]", output) if output is not None else sys.stdout
)
Loading