diff --git a/graphite_beacon/core.py b/graphite_beacon/core.py
index 88fadfa..adef419 100644
--- a/graphite_beacon/core.py
+++ b/graphite_beacon/core.py
@@ -2,6 +2,7 @@ import os
 from re import compile as re, M
 
 import json
+import logging
 from tornado import ioloop, log
 
 from .alerts import BaseAlert
@@ -56,7 +57,7 @@ class Reactor(object):
         for config in self.options.pop('include', []):
             self.include_config(config)
 
-        LOGGER.setLevel(self.options.get('logging', 'info').upper())
+        LOGGER.setLevel(_get_numeric_log_level(self.options.get('logging', 'info')))
         registry.clean()
 
         self.handlers = {'warning': set(), 'critical': set(), 'normal': set()}
@@ -123,3 +124,33 @@ class Reactor(object):
 
         for handler in self.handlers.get(level, []):
             handler.notify(level, alert, value, target=target, ntype=ntype, rule=rule)
+
+
+_LOG_LEVELS = {
+    'DEBUG': logging.DEBUG,
+    'INFO': logging.INFO,
+    'WARN': logging.WARN,
+    'WARNING': logging.WARNING,
+    'ERROR': logging.ERROR,
+    'CRITICAL': logging.CRITICAL
+}
+
+
+def _get_numeric_log_level(name):
+    """Convert a textual log level to the numeric constants expected by the
+    :meth:`logging.Logger.setLevel` method.
+
+    This is required for compatibility with Python 2.6 where there is no conversion
+    performed by the ``setLevel`` method. In Python 2.7 textual names are converted
+    to numeric constants automatically.
+
+    :param basestring name: Textual log level name
+    :return: Numeric log level constant
+    :rtype: int
+    """
+    name = str(name).upper()
+
+    try:
+        return _LOG_LEVELS[name]
+    except KeyError:
+        raise ValueError("Unknown level: %s" % name)

Logging configuration only works in Python 2.7+
Hi,

The way log level is set in [`Reactor`](https://github.com/klen/graphite-beacon/blob/develop/graphite_beacon/core.py#L59) requires that textual log levels ("INFO", "WARN") are converted to the numeric log levels by the `logging.Logger.setLevel` method. This is only the case in Python 2.7.

You can see the change between 2.6 and 2.7 here:

2.6 - https://github.com/python/cpython/blob/2.6/Lib/logging/__init__.py#L1032
2.7 - https://github.com/python/cpython/blob/2.7/Lib/logging/__init__.py#L1136

I'll submit a pull request shortly. Thanks!
diff --git a/tests.py b/tests.py
index 7bcd15a..9d5458a 100644
--- a/tests.py
+++ b/tests.py
@@ -1,5 +1,6 @@
 """ TODO: Implement the tests. """
 
+import logging
 import pytest
 import mock
 
@@ -24,6 +25,28 @@ def test_reactor():
     assert len(rr.alerts) == 2
 
 
+def test_convert_config_log_level():
+    from graphite_beacon.core import _get_numeric_log_level
+
+    assert logging.DEBUG == _get_numeric_log_level('debug')
+    assert logging.DEBUG == _get_numeric_log_level('DEBUG')
+
+    assert logging.INFO == _get_numeric_log_level('info')
+    assert logging.INFO == _get_numeric_log_level('INFO')
+
+    assert logging.WARN == _get_numeric_log_level('warn')
+    assert logging.WARN == _get_numeric_log_level('WARN')
+
+    assert logging.WARNING == _get_numeric_log_level('warning')
+    assert logging.WARNING == _get_numeric_log_level('WARNING')
+
+    assert logging.ERROR == _get_numeric_log_level('error')
+    assert logging.ERROR == _get_numeric_log_level('ERROR')
+
+    assert logging.CRITICAL == _get_numeric_log_level('critical')
+    assert logging.CRITICAL == _get_numeric_log_level('CRITICAL')
+
+
 def test_alert(reactor):
     from graphite_beacon.alerts import BaseAlert, GraphiteAlert, URLAlert
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest mock
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
exceptiongroup==1.2.2
-e git+https://github.com/klen/graphite-beacon.git@66ec0720ca5a3a9d43a63fa0a60b3eb6e26e4157#egg=graphite_beacon
iniconfig==2.1.0
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
tomli==2.2.1
tornado==4.0.2

name: graphite-beacon
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - certifi==2025.1.31
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - tomli==2.2.1
      - tornado==4.0.2
prefix: /opt/conda/envs/graphite-beacon

tests.py::test_convert_config_log_level
tests.py::test_reactor tests.py::test_convert tests.py::test_parse_interval tests.py::test_interval_to_graphite tests.py::test_parse_rule
graphite_beacon/core.py
graphite_beacon/core.py