---
title: "Siapkan logging Python dengan level log yang tepat dan penangkapan exception"
description: "Python logging hanya mencetak WARNING ke atas sampai kamu mengonfigurasinya. Ini penyiapan 3 baris, 5 level log, dan cara mencatat exception beserta traceback-nya, dari pengujian di Python 3.13."
canonical: "https://logbrew.co/id/guides/python-logging"
last_updated: "2026-11-10"
---

# Siapkan logging Python dengan level log yang tepat dan penangkapan exception

Python `logging` hanya mencetak WARNING ke atas sampai kamu mengonfigurasinya. Ini penyiapan 3 baris, 5 level log, dan cara mencatat exception beserta traceback-nya, dari pengujian di Python 3.13.

Diuji pada 10 November 2026 dengan Python 3.13.5 dan logbrew-sdk 0.1.29

Oleh [Furkan Erday](https://github.com/furkanerday). Furkan Erday adalah pendiri LogBrew. Setiap hari ia membangun LogBrew dengan Claude Code dan Codex.

Canonical: https://logbrew.co/id/guides/python-logging

## Apa yang dilakukan tiap level log Python tanpa penyiapan, dan di LogBrew

| Panggilan logging | Dicetak tanpa penyiapan | Level sendiri di LogBrew |
| --- | --- | --- |
| `log.debug()` | Dibuang | Digabung ke `info` |
| `log.info()` | Dibuang | `info` |
| `log.warning()` | `WARNING:root:` | `warning` |
| `log.error()` | `ERROR:root:` | `error` |
| `log.critical()` | `CRITICAL:root:` | `critical` |

5 panggilan ke root logger tanpa konfigurasi, lalu 5 panggilan yang sama lewat handler LogBrew. Cangkir penuh berarti dicetak, atau disimpan sebagai level sendiri.

## Siapkan logging dalam 3 baris

### Panggil basicConfig() sekali, sebelum panggilan log pertama

`basicConfig()` mengatur level dan format baris root logger. Tanpa penyiapan, Python hanya mencetak WARNING ke atas, seperti `WARNING:root:keg below 10%`, dan membuang DEBUG dan INFO.

```bash
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
log = logging.getLogger("taproom")

log.debug("keg pressure read")
log.info("tap opened")
log.warning("keg below 10%")
log.error("keg empty")
log.critical("cooler offline")
```

Output

```text
2026-11-10 08:34:04,828 INFO taproom tap opened
2026-11-10 08:34:04,828 WARNING taproom keg below 10%
2026-11-10 08:34:04,828 ERROR taproom keg empty
2026-11-10 08:34:04,828 CRITICAL taproom cooler offline
```

### Beri nama logger sesuai tiap modul

`logging.getLogger(__name__)` memberi tiap modul logger-nya sendiri, jadi setiap baris menyebut modul asalnya. Pakai root logger hanya untuk `basicConfig()`.

### Baca level dari variabel lingkungan

`basicConfig()` menerima nama level sebagai teks. Jalankan ini dengan `LOG_LEVEL=DEBUG` dan baris debug muncul, tanpa mengubah kode.

```bash
import logging
import os

logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
log = logging.getLogger("taproom")
log.debug("keg pressure read")
log.info("tap opened")
```

Output

```text
DEBUG:taproom:keg pressure read
INFO:taproom:tap opened
```

### Berikan force=True untuk mengonfigurasi ulang

Python mengabaikan panggilan `basicConfig()` kedua saat root logger sudah punya handler. Berikan `force=True` untuk mengganti handler-nya, misalnya di pengujian.

```bash
import logging

logging.basicConfig(level=logging.WARNING)
logging.basicConfig(level=logging.DEBUG)
logging.debug("second basicConfig: not printed")
logging.basicConfig(level=logging.DEBUG, force=True)
logging.debug("basicConfig with force=True: printed")
```

Output

```text
DEBUG:root:basicConfig with force=True: printed
```

## Catat setiap exception beserta traceback-nya

### Panggil log.exception() di dalam except

`log.exception()` mencatat di level ERROR dan menambahkan traceback. Berikan `exc_info=True` ke panggilan di level lain untuk melakukan hal yang sama di sana.

```bash
import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
log = logging.getLogger("taproom")
taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed")
```

Output

```text
ERROR taproom pour failed
Traceback (most recent call last):
  File "app.py", line 11, in <module>
    pour("stout")
    ~~~~^^^^^^^^^
  File "app.py", line 8, in pour
    return taps[beer]
           ~~~~^^^^^^
KeyError: 'stout'
```

### Kirim tiap baris ke LogBrew dengan handler logging

Tambahkan `LogBrewLoggingHandler` ke logger kamu. Tiap record menjadi log LogBrew dengan level, nama logger, file, dan barisnya. Nama dan pesan exception ikut terkirim, begitu juga nilai di `extra={}`.

```bash
import logging
import os

from logbrew_sdk import HttpTransport, LogBrewClient, LogBrewLoggingHandler

client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"])
transport = HttpTransport()
log = logging.getLogger("taproom")
log.addHandler(LogBrewLoggingHandler(client, transport, metadata={"service": "taproom"}))
log.setLevel(logging.INFO)

taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed", extra={"order_id": "ord_42"})

response = client.flush(transport)
print("HTTP", response.status_code, "accepted", response.accepted_events, "logs")
```

Output

```text
HTTP 200 accepted 1 logs
```

### Baca kembali log beserta field exception-nya

`logbrew read logs --severity error --json` mengembalikan baris dengan `exceptionName`, `exceptionMessage`, file, fungsi, nomor baris, dan `order_id` kamu.

```bash
logbrew read logs --project <project_id> --severity error --since 1h --json | jq '.[0] | {level, message, logger: .attributes.logger, metadata: .attributes.metadata}'
```

Output

```text
{
  "level": "error",
  "message": "pour failed",
  "logger": "taproom",
  "metadata": {
    "exceptionMessage": "'stout'",
    "exceptionName": "KeyError",
    "fileName": "send.py",
    "functionName": "<module>",
    "levelName": "ERROR",
    "levelNumber": 40,
    "lineNumber": 20,
    "module": "send",
    "order_id": "ord_42",
    "processName": "MainProcess",
    "service": "taproom",
    "threadName": "MainThread"
  }
}
```

### Tangkap crash yang menghentikan proses

`install_process_error_capture()` mengaitkan diri ke `sys.excepthook`. Exception yang tidak ditangkap siapa pun menjadi masalah di level `critical` dengan `handled: false`, direkam sebelum proses keluar dengan kode 1.

```bash
import atexit
import json
import os

from logbrew_sdk import LogBrewClient, RecordingTransport, install_process_error_capture

transport = RecordingTransport.always_accept()
client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"], transport=transport)
install_process_error_capture(client, on_capture_error=print)


@atexit.register
def show_captured_issue() -> None:
    issue = json.loads(transport.last_body())["events"][0]["attributes"]
    print(json.dumps({k: issue[k] for k in ("title", "level")}), json.dumps(issue["exception"]))


kegs = {"ipa": 3}
kegs["stout"] -= 1
```

Output

```text
Traceback (most recent call last):
  File "<stdin>", line 19, in <module>
KeyError: 'stout'
{"title": "KeyError", "level": "critical"} {"type": "KeyError", "mechanism": {"type": "python.excepthook", "handled": false}}
```

## Hasil pengujian kami

Tanpa konfigurasi, 5 panggilan ke root logger mencetak 3 baris ke stderr dan membuang panggilan DEBUG dan INFO. Baris bawaannya adalah level, nama logger, dan pesan, seperti `WARNING:root:keg below 10%`.

Panggilan `basicConfig()` kedua tidak mengubah apa pun sampai kami memberikan `force=True`. Lewat handler LogBrew, record DEBUG dan INFO tiba sebagai `info`, WARNING sebagai `warning`, ERROR sebagai `error`, dan CRITICAL sebagai `critical`.

`log.exception()` menambahkan `exceptionName` dan `exceptionMessage` ke log LogBrew. Dengan `include_exception_text=True` di handler, traceback lengkap tiba sebagai `exceptionText`.

```bash
import logging

logging.debug("keg pressure read")
logging.info("tap opened")
logging.warning("keg below 10%")
logging.error("keg empty")
logging.critical("cooler offline")
```

Output

```text
WARNING:root:keg below 10%
ERROR:root:keg empty
CRITICAL:root:cooler offline
```

## 5 level log dan kapan memakai masing-masing

DEBUG untuk nilai yang kamu butuhkan saat memperbaiki bug, seperti tekanan keg, teks SQL, atau jumlah percobaan ulang. Matikan di produksi.

INFO mencatat apa yang dilakukan program, seperti tap dibuka atau pesanan dibayar. WARNING mencatat apa yang salah tapi sudah ditangani, seperti keg di bawah 10%. ERROR mencatat permintaan atau job yang gagal, seperti keg kosong. CRITICAL mencatat apa yang menghentikan program, seperti pendingin mati.

Catat tiap exception sekali, di tempat kamu menanganinya. 1 baris di setiap tingkat call stack mengirim 3 peringatan untuk 1 kegagalan.

```bash
import logging

for name in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
    print(name, logging.getLevelName(name))
print("root logger level:", logging.getLevelName(logging.getLogger().level))
```

Output

```text
DEBUG 10
INFO 20
WARNING 30
ERROR 40
CRITICAL 50
root logger level: WARNING
```

## Apa yang direkam LogBrew

Tiap record menjadi 1 log dengan level, nama logger, nama file, fungsi, nomor baris, dan modul. Nilai primitif dari `extra={}` menjadi field yang bisa kamu filter, dan `metadata={}` di handler menambahkan field ke setiap baris, seperti `service` di atas.

Saat ada exception, log mendapat `exceptionName` dan `exceptionMessage`. Berikan `include_exception_text=True` ke handler untuk menyimpan teks traceback juga.

`install_process_error_capture()` mengubah exception yang tidak tertangkap menjadi masalah di level `critical`, dengan tipe exception, frame stack, dan mekanisme `python.excepthook`.

## Pertanyaan yang sering diajukan

### Apa level logging bawaan di Python?

WARNING. Tanpa panggilan `basicConfig()`, root logger mencetak WARNING, ERROR, dan CRITICAL ke stderr sebagai `WARNING:root:message`, dan membuang DEBUG dan INFO.

### Kenapa logging.basicConfig tidak melakukan apa-apa?

Root logger sudah punya handler, dari panggilan `basicConfig()` sebelumnya atau dari library, jadi Python mengabaikan panggilannya. Berikan `force=True` untuk mengganti handler-nya.

### Apa bedanya logging.error dan logging.exception?

`log.exception()` adalah `log.error()` dengan `exc_info=True`. Ia mencatat di level ERROR dan menambahkan traceback. Panggil hanya di dalam blok `except`.

### Apa urutan level logging Python?

DEBUG 10, INFO 20, WARNING 30, ERROR 40, CRITICAL 50. Logger meloloskan record saat angkanya sama atau lebih tinggi dari level yang kamu atur.

### Pakai print() atau logging di Python?

Pakai `logging`. `print()` tidak punya level, stempel waktu, nama logger, atau handler. Dengan `logging` kamu menyalakan baris per modul dan mengirimnya ke file atau ke LogBrew.

## Lihat setiap exception Python beserta traceback-nya

SDK Python LogBrew mengirim tiap baris log dengan level, logger, file, dan barisnya, dan mengubah exception yang tidak tertangkap menjadi masalah dengan stack trace-nya. Mulai dengan paket gratis yang lega, tanpa perlu kartu.

[Mulai gratis](https://logbrew.co/id)

## Sumber

- [Python docs, Logging HOWTO](https://docs.python.org/3/howto/logging.html)
- [Python docs, logging module](https://docs.python.org/3/library/logging.html)
- [python/cpython#78078](https://github.com/python/cpython/issues/78078)

## Panduan lain untuk agen dan Python

- [Tambahkan server MCP ke Claude Code dan Codex](https://logbrew.co/id/guides/mcp-server-claude-code-codex)
- [Pakai AGENTS.md di Claude Code dan Codex](https://logbrew.co/id/guides/agents-md)
- [Atasi error Claude overloaded 529 dan rate limit 429](https://logbrew.co/id/guides/claude-overloaded-rate-limit-error)
- [Logging terstruktur dengan format log dan level log yang bisa dicari per field](https://logbrew.co/id/guides/structured-logging)
- [Cara mengatasi error di Claude Code, Codex, JavaScript, dan Python](https://logbrew.co/id/guides)
