# Lescopr Python SDK

[![PyPI version](https://img.shields.io/pypi/v/lescopr.svg)](https://pypi.org/project/lescopr/)
[![PyPI downloads](https://img.shields.io/pypi/dm/lescopr.svg)](https://pypi.org/project/lescopr/)
[![Python versions](https://img.shields.io/pypi/pyversions/lescopr.svg)](https://pypi.org/project/lescopr/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

**Lescopr** is a zero-configuration Python monitoring SDK that automatically captures logs, errors, and system metrics from any Python project and streams them in real-time to the [Lescopr dashboard](https://app.lescopr.com).

It works out of the box with **Django**, **Flask**, **FastAPI**, and any custom Python architecture — no framework-specific code required.

---

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Framework Integration](#framework-integration)
  - [Django](#django)
  - [Flask](#flask)
  - [FastAPI](#fastapi)
  - [Generic Python / Custom Architecture](#generic-python--custom-architecture)
- [Architecture](#architecture)
- [CLI Reference](#cli-reference)
- [Advanced Configuration](#advanced-configuration)
- [How It Works](#how-it-works)
- [PyPI](#pypi)
- [Contributing](#contributing)
- [License](#license)

---

## Features

- ✅ **Automatic log capture** — hooks into Python's `logging` module without any code changes
- ✅ **Framework auto-detection** — detects Django, Flask, FastAPI, and more from your project files
- ✅ **Real-time metrics** — streams system and application metrics via gRPC
- ✅ **Error tracking** — captures exceptions, import errors, and tracebacks
- ✅ **Daemon architecture** — runs as a background process, completely non-blocking
- ✅ **Zero configuration** — a single CLI command is enough to get started
- ✅ **Works everywhere** — Django, Flask, FastAPI, Celery, scripts, custom architectures

---

## Installation

```bash
pip install lescopr
```

> **Requirements:** Python 3.8 or later.

Optional dependency for daemon process detection:

```bash
pip install lescopr[psutil]
```

---

## Quick Start

**Step 1 — Initialise the SDK in your project directory:**

```bash
lescopr init --sdk-key YOUR_SDK_KEY
```

This auto-detects your framework, registers the project with the Lescopr API, and starts the background daemon.

**Step 2 — Import and activate monitoring in your application:**

```python
import lescopr
lescopr.logs()
```

That's it. All subsequent log calls are forwarded to the Lescopr dashboard automatically.

---

## Framework Integration

### Django

Add a single line to your `settings.py` (before any other app-specific imports):

```python
# settings.py
import lescopr
lescopr.logs()

INSTALLED_APPS = [
    ...
]
```

The SDK automatically hooks into Django's logging system and reconnects after any hot-reload. It also captures uncaught exceptions via Django's `got_request_exception` signal.

**WSGI/ASGI entry points** (`wsgi.py` / `asgi.py`) work just as well if you prefer:

```python
# wsgi.py
import lescopr
lescopr.logs()

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
```

### Flask

Add monitoring at the top of your application factory or `app.py`:

```python
# app.py
import lescopr
lescopr.logs()

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World'
```

Works with Flask's development server, Gunicorn, uWSGI, and any other WSGI server.

### FastAPI

Add monitoring in your `main.py` before instantiating the `FastAPI` app:

```python
# main.py
import lescopr
lescopr.logs()

from fastapi import FastAPI
app = FastAPI()

@app.get('/')
async def root():
    return {'message': 'Hello World'}
```

Compatible with Uvicorn, Hypercorn, and any other ASGI server.

### Generic Python / Custom Architecture

The SDK is **framework-agnostic**. For scripts, Celery workers, background tasks, or any custom Python architecture, add the same two lines:

```python
import lescopr
lescopr.logs()

# Your existing code continues here
import logging
logger = logging.getLogger(__name__)

logger.info('Application started')   # → forwarded to Lescopr
logger.error('Something went wrong') # → forwarded to Lescopr
```

Because `lescopr.logs()` patches Python's built-in `logging` module and `builtins.__import__`, it captures logs and import errors from **any** Python code, regardless of the framework in use.

---

## Architecture

```
Your Python Application
        │
        │  logging.getLogger(...).info(...)
        ▼
┌─────────────────────────────┐
│   ResilientLescoprHandler   │  (attached to root logger)
│   + import error hooks      │
└──────────────┬──────────────┘
               │ gRPC stream
               ▼
┌─────────────────────────────┐
│      Lescopr Daemon         │  (background process, .lescopr.pid)
│  lescopr/daemon/main.py     │
└──────────────┬──────────────┘
               │ gRPC / HTTPS
               ▼
     https://api.lescopr.com
               │
               ▼
     Lescopr Dashboard
  https://app.lescopr.com
```

**Key components:**

| Component | Path | Role |
|---|---|---|
| `Lescopr` (core) | `lescopr/core/lescopr.py` | Central SDK object, config management, daemon control |
| `logs()` | `lescopr/__init__.py` | Auto-discovery entry point, installs log handler |
| `FrameworkAnalyzer` | `lescopr/filesystem/analyzers/framework.py` | Detects Django, Flask, FastAPI, etc. |
| `GrpcClient` | `lescopr/network/grpc/client.py` | Streams logs to the daemon via gRPC |
| Daemon | `lescopr/daemon/main.py` | Background process that relays data to the API |
| CLI | `lescopr/cli.py` | `init`, `start`, `stop`, `status`, `diagnose`, `reset` |

The daemon uses a **bidirectional gRPC stream** so log delivery is non-blocking and resilient to transient network failures.

---

## CLI Reference

After installation, the `lescopr` command is available in your terminal:

```
lescopr [OPTIONS] COMMAND [ARGS]...
```

| Command | Description |
|---|---|
| `lescopr init --sdk-key KEY` | Initialise the SDK in the current project |
| `lescopr start` | Start the monitoring daemon |
| `lescopr stop` | Stop the monitoring daemon |
| `lescopr status` | Show daemon status and project info |
| `lescopr diagnose` | Run a full connectivity diagnostic |
| `lescopr reset` | Remove SDK configuration and stop daemon |

### `init`

```bash
lescopr init --sdk-key YOUR_SDK_KEY [--environment production|development] [--no-start-daemon]
```

- Auto-detects your project framework (Django, Flask, FastAPI, etc.)
- Registers the project with the Lescopr API
- Saves credentials to `.lescopr.json`
- Starts the background daemon (pass `--no-start-daemon` to skip)

### `diagnose`

```bash
lescopr diagnose --verbose --check-server
```

Prints a full report of the configuration, daemon state, and (optionally) gRPC server connectivity.

---

## Advanced Configuration

`lescopr.logs()` reads from `.lescopr.json` automatically. You can also pass parameters directly to `Lescopr`:

```python
from lescopr import Lescopr

client = Lescopr(
    api_key="YOUR_API_KEY",        # Required – your account API key
    sdk_key="YOUR_SDK_KEY",        # Required – your project SDK key
    environment="production",      # "development" (default) or "production"
    auto_logging=True,             # Attach to root logger immediately (default: True)
    auto_grpc=True,                # Connect to daemon immediately (default: True)
)
```

### Environment variables

| Variable | Description |
|---|---|
| `LESCOPR_DAEMON_MODE=true` | Prevents the SDK from starting inside its own daemon process |

### `.lescopr.json` (generated by `lescopr init`)

```json
{
  "sdk_id": "proj_xxxx",
  "sdk_key": "lsk_xxxx",
  "api_key": "lak_xxxx",
  "environment": "development",
  "project_name": "my-app",
  "project_stack": ["django"]
}
```

> **Security:** Add `.lescopr.json` to your `.gitignore` to avoid committing credentials.

### Debug mode

```python
import lescopr
lescopr.set_debug_mode()   # enables verbose internal prints
lescopr.logs()
```

---

## How It Works

1. **`lescopr init`** scans your project files, detects the framework, and calls the Lescopr API to register the project. It writes `.lescopr.json` and starts a background daemon process.

2. **`lescopr.logs()`** (called at app startup) checks for `.lescopr.json` and a running daemon, then installs a `ResilientLescoprHandler` on Python's root logger. All existing `logging.getLogger(...)` calls in your codebase are captured automatically — no changes required.

3. **The daemon** (`lescopr-daemon`) runs as an independent background process and maintains a persistent gRPC connection to `api.lescopr.com`. Log entries are batched and streamed in real-time.

4. **Framework-specific integrations** are applied automatically:
   - **Django:** monkey-patches `configure_logging` so the handler survives hot-reloads; connects to the `got_request_exception` signal.
   - **Flask / FastAPI:** the root-logger hook is sufficient — no extra middleware needed.
   - **Custom Python:** the same root-logger hook works for any framework or plain scripts.

---

## PyPI

The package is available on PyPI: **[lescopr](https://pypi.org/project/lescopr/)**

To publish a new release:

```bash
# Build source distribution and wheel
python -m build

# Upload to PyPI
twine upload dist/*
```

> Requires `build` and `twine`: `pip install build twine`

---

## Contributing

1. Fork the repository: <https://github.com/Lescopr/lescopr-python>
2. Create a feature branch: `git checkout -b feature/my-feature`
3. Commit your changes: `git commit -m "Add my feature"`
4. Push and open a Pull Request

Please open an issue before starting significant work so we can discuss the approach.

---

## Support

| Channel | Link |
|---|---|
| 📖 Documentation | <https://docs.lescopr.com> |
| 🌐 Dashboard | <https://app.lescopr.com> |
| 📧 Email | <support@lescopr.com> |
| 🐛 Bug reports | <https://github.com/Lescopr/lescopr-python/issues> |

---

## License

[MIT](LICENSE) © 2024-present [SonnaLab](https://sonnalab.com)
