Контекст отладочных сообщений

Dmitriy82

Допустим, есть такой код:
def process_wheel(wheel):
if problem with the wheel:
log(problem with the wheel)
def process_bicycle(bicycle):
for wheel in bicycle.wheels:
process_wheel(wheel)
...
for bicycle in bicycles:
process_bicycle(bicycle)

Неудобство в том, что напечатается проблематичное колесо, но не будет понятно к какому велосипеду оно относится.
Что можно сделать?
Можно всегда печатать текущий велосипед. Но их может быть много.
Можно передавать велосипед дополнительным аргументом в process_wheel, но это как-то мерзко потому что он нужен исключительно в отладочных целях.
Можно сделать process_wheel методом класс BicycleProcessor, у которого в состоянии есть current_bicycle. Но это ООП.
Вдобавок, недостаток последних двух подходов в том, что колёса бывают не только у велосипедов.
Мне недавно пришла в голову такая идея. Ввести контекст-менеждер, который будет содержать отладочный, хм, контекст. Функция печатающая сообщение проверяет, в каких контексах она находится (динамически и если данные контекста ещё не печатались, печатает их непосредственно перед тем сообщением, которое ей передали.
Типа, код будет выглядеть так:

def process_bicycle(bicycle):
with LogContext(bicycle):
for wheel in bicycle.wheels:
process_wheel(wheel)

А печатать будет примерно следующее:

in the context bicycleproblem with the wheelprobelm with the wheelin the context bicycleproblem with the wheelhr />

Бонус: контексты могут быть вложенными.
Получаются типа такие domain-specific stack traces.
---
Никакого вопроса тут нет, просто решил поделиться.

serega1604

я так понял ты решил переизобрести MDC/NDC?

Dmitriy82

Похоже что да. А NDC умеет печататься как у меня в примере, или только тупо
in the context bicycleproblem with the wheelin the context bicycleprobelm with the wheelin the context bicycleproblem with the wheelhr />

?

serega1604

Это уже от appender-а зависит, если он умеет не выводить повторяющийся контекст дважды, то почему бы и нет? Ну а вообще если у нас несколько потоков и в лог не выводится id потока, то такая оптимизация может выйти боком.

bleyman

Я об этой проблеме много думал.
Я думал о ней так: я хочу видеть в стектрейсе упавшей прожрамки значения интересных переменных. Или в эксепшене, если прожрамка упала не совсем. Причём эти переменные и их значения выводятся для всех стекфреймов в стектрейсе.
Твоё решение мне в общем нравится, но только оно будет приводить к сильно повышенному нестингу. Вот если б я это писал на плюсах, решение было бы довольно очевидным: взять макрос для уникального имени переменной ("logging_context" + __FILE__ + __LINE__ покатит дальше RAII и добавлять значения при помощи WITH_CONTEXT(wheel.id) (которое __FILE__ и __LINE__ тоже в сообщение добавит отдельно).
Питон может в RAII-style inline "do something in current scope, undo that when leaving the scope" без дичайших извращений?

Dmitriy82

В с++ прикольно получается!
Я бы предпочёл группировать переменные по domain specific фреймам, а не получать их длинным однородным списком, но это тоже легко делается добавлением ещё одной raii-штуки.
Но что именно тебя в питоне смущает, что надо в четыре пробела отступ делать после with? Так это же не на каждую отладочную переменную, а на каждый контекст только.
Ну и в любом случае можно так:
@interesting_pseudo_frame
def process_bicycle(bicycle):
interesting_value("bicycle.id", bicycle.id)
...

bleyman

Эм, а как ты это сделаешь, interesting_value динамически не появится в обёрнутой функции. Или ты совсем кривой хак имеешь в виду?
Но это навело меня на мысль кстати, что наверное хочется обычно сохранять именно значения аргументов функции (ибо если кому нужно вычисленное сохранить — пусть сохраняет в вызываемой функции). И вот тут вполне уместно сделать декоратор который их сохраняет. В третьем питоне (я не знаю, его нормальные люди уже используют?) можно даже как бы теги добавлять к аргументам.
> Но что именно тебя в питоне смущает, что надо в четыре пробела отступ делать после with?
Да, дико смущает. Код должен быть красивым, функциональность которая требует уродования кода сосёт.

beluchy

Функция печатающая сообщение проверяет, в каких контексах она находится (динамически и если данные контекста ещё не печатались, печатает их непосредственно перед тем сообщением, которое ей передали.
как эта функция будет обращаться к сохраненному контекст? где будет храниться этот контекст?

beluchy

я хочу видеть в стектрейсе упавшей прожрамки значения интересных переменных. Или в эксепшене, если прожрамка упала не совсем. Причём эти переменные и их значения выводятся для всех стекфреймов в стектрейсе.
это немного другая задача, чем озвученная ТС, но все же:

class MyError(Exception):
def __init__(self, tr=[]):
self.tr = tr

def deco(f):
def df(*args):
try:
return f(*args)
except MyError,e:
raise MyError(e.tr + [(f.__name__,args)])
return df

# test implementation
@deco
def f1(a):
raise MyError

@deco
def f2(a):
f1(3)

@deco
def f3(a,b):
f2(34)

try:
f3(100,500)
except MyError,e:
print e.tr

## -- End pasted text --
Out:
[('f1', (3 ('f2', (34 ('f3', (100, 500]

6yrop

Код должен быть красивым, функциональность которая требует уродования кода сосёт.
Тебе удается продавать этот поинт?
Оставить комментарий
Имя или ник:
Комментарий: