Потокобезопасный регистратор для Tcl - PullRequest
1 голос
/ 07 ноября 2011

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

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

Спасибо

Ответы [ 4 ]

3 голосов
/ 07 ноября 2011

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

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

Обновление Хорошо, вот рабочий набросок того, что я фактически предложил реализовать:

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 хорошо разделены и обычно обмениваются сообщениями.
  • Следовательно, создайте поток для регистратора и научите рабочие потоки отправлять ему сообщения;поэтому рабочие потоки не имеют отношения к тому, как реализован регистратор.

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

1 голос
/ 07 ноября 2011

A Logging API для Tcl

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

0 голосов
/ 09 ноября 2011

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

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

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

0 голосов
/ 07 ноября 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...