Поточно-безопасный регистратор для Tcl

Мне нужна библиотека журналов для моего многопоточного приложения Tcl. Могу ли я использовать стандартный logger пакет? Если можно, какие ограничения применяются в многопоточном окружении?

Если возможно, я хотел бы поделиться службами ведения журналов между потоками.

Спасибо


person Andrey    schedule 07.11.2011    source источник
comment
tcllib.sourceforge.net/doc/logger.html также ознакомьтесь с code.activestate.com/lists/tcl-core/10866   -  person bilash.saha    schedule 07.11.2011
comment
Первая ссылка относится к некоторому настраиваемому регистратору, который слишком плохо описан для использования. Вторая ссылка относится к стандартной библиотеке регистратора, о которой я упоминал в своем вопросе. Действительно ли это потокобезопасно?   -  person Andrey    schedule 07.11.2011


Ответы (4)


Потоки Tcl не обмениваются данными (если вы явно не используете определенные средства из пакета Thread), а вместо этого общаются посредством передачи сообщений. Таким образом, похоже, что можно было бы настроить выделенный поток «регистратора» и просто поставить в него сообщения журнала из рабочих потоков.

В противном случае спорная точка, вероятно, будет где-то в ресурсе ОС, используемом регистратором для фактической записи данных.

Обновление. Вот рабочий набросок того, что я на самом деле предлагал реализовать:

package require Tcl 8.5
package require Thread

proc make_worker_thread {logger_id body} {
  set newbody [list set ::logger $logger_id]
  append newbody \n {
    proc ::log {severity msg} {
      global logger
      thread::send $logger [list ::log $severity $msg]
    }
  } \n $body
  thread::create $newbody
}

set logger [thread::create {
  package require logger

  proc log {severity msg} {
    puts "hey, that's it: ($severity) $msg"
  }

  puts "logger thread created: [thread::id]"

  thread::wait
}]

for {set i 0} {$i < 3} {incr i} {
  make_worker_thread $logger {
    proc post_msg {} {
      log notice "A message from [thread::id]"
        after 1000 ::post_msg
    }

    puts "worker thread created: [thread::id]"

    after 1000 ::post_msg

    thread::wait
  }
}

vwait forever

Этот код создает один поток регистратора и четыре рабочих потока, каждый из которых отправляет сообщение потоку регистратора один раз в секунду. Код выполняется до тех пор, пока не будет прерван вручную. Поток регистратора просто выводит сообщение, которое оно было передано в консоль, но, как уже упоминал кто-то другой в этом потоке, вы, вероятно, могли бы использовать пакет "logger" от Tcllib, если вам нужны необычные вещи, такие как средства.

Повторюсь:

  • Сам пакет регистратора, по-видимому, ничего не знает о многопоточности.
  • Потоки Tcl хорошо разделены и обычно общаются посредством передачи сообщений.
  • Следовательно, создайте поток для регистратора и научите рабочие потоки отправлять ему сообщения; поэтому рабочие потоки не заботятся о том, как реализован регистратор.

P.S. В рабочих потоках вы можете использовать [thread::send -async ...], чтобы сделать отправку сообщений журнала полностью асинхронной.

person kostix    schedule 07.11.2011
comment
Спасибо. Похоже, это решение. Знаете ли вы какие-либо существующие и надежные реализации такого выделенного потока журналов? - person Andrey; 07.11.2011
comment
@ Андрей, зачем тебе это? Кажется, это несложно: в основном вы создаете новый поток, а затем в нем загружаете этот пакет регистратора, загружаете его и затем входите в цикл событий. Другие потоки используют thread::send в идентификаторе этого потока. Начать с wiki.tcl.tk/Thread - person kostix; 07.11.2011
comment
Я хочу интегрировать пакет логгеров с многопоточностью. И я не хочу исследовать внутренности регистратора, если кто-то уже проделал эту работу. - person Andrey; 07.11.2011
comment
@ Андрей, кажется, ты меня не понял. Я предложил не интегрировать пакет регистратора с многопоточностью, а скорее использовать способ Tcl для многопоточности, чтобы регистратор вообще не интересовался многопоточностью. Я обновил свой пост рабочей программой, демонстрирующей концепции. - person kostix; 07.11.2011

API ведения журналов для Tcl

Эта реализация потокобезопасна. Из-за общего назначения C-функции не требуют tcl-интерпретатора.

person bilash.saha    schedule 07.11.2011
comment
Спасибо за ответ. Где найти ссылки на скачиваемый архив и документацию? Должен ли я связываться с разработчиком по его электронной почте? - person Andrey; 07.11.2011
comment
@ bilash.saha, это для пакета регистратора от Tcllib. На самом деле я спрашивал об этом посте tcl-core ;-) - person kostix; 07.11.2011

Это немного зависит от того, чего вы хотите достичь с помощью многопоточного регистратора.

Если у вас просто есть вариант использования, чтобы не блокировать ваши рабочие потоки при записи сообщений журнала на диск, самый простой способ - использовать обычный журнал и настроить простой logproc, который выполняет thread :: send -async в некоторый поток журнала (который сам может используйте регистратор с приложениями для записи фактических файлов журнала) с вашим сообщением журнала (в основном то, что было набросано в принятом ответе).

Если вы хотите использовать опцию логгеров, чтобы отключить / включить ведение журнала для всей программы в различных потоках, вам нужно проделать немного больше работы, чтобы распространить изменения уровня журнала на все потоки с помощью настраиваемого lvlchangeproc.

person schlenk    schedule 09.11.2011

Вот моя "многопоточная" обертка для пакета logger:

# replacement for logger::init procedure from logger package
proc ::mylogger::init { service } {
  set log [logger::init $service]

  foreach lvl [logger::levels] {
    interp alias {} log_to_file_$lvl {} ::mylogger::log $lvl $service
    ${log}::logproc $lvl log_to_file_$lvl
  }

  return $log
}

proc mylogger::server { } {
  set t [thread::create {

    proc log { level txt } {
        set msg "\[[clock format [clock seconds] -format "%Y-%m-%dT%H:%M:%S"]\]\t$level\t$txt"
        puts stderr $msg
    }

    # enter to event loop
    thread::wait
  }]

  tsv::set measure-logger loggerThread $t
}

proc ::mylogger::log { level service txt } {
  set t [tsv::get measure-logger loggerThread]
  thread::send -async $t [list log $level "$service\t$txt"]
}

# EXAMPLE

# start logging thread
# should be called once from main application thread
::mylogger::server

# create logger
# may be called from any thread
set log [mylogger::init myservice]

# log a message
# may be called from the thread the "mylogger::init myservice" was called in
${log}::debug myservice "Hello, World!"

# wait a second
after 1000
person Andrey    schedule 07.11.2011
comment
Наверное работает. Подход tsv::get в ::mylogger:log выглядит немного сомнительным, я бы просто создал настраиваемую процедуру с правильным встроенным идентификатором потока вместо того, чтобы вызывать tsv::get при каждом вызове журнала. Самый простой способ сделать это - в ::mylogger::init процессе, где вы все равно настраиваете псевдонимы журналирования, вы можете просто добавить идентификатор потока в качестве фиксированного 4-го параметра к псевдониму. - person schlenk; 09.11.2011