c++ - protocol - syslog windows
Redirecciona C++ std:: clog a syslog en Unix (4)
Trabajo en Unix en un programa C ++ que envía mensajes a syslog.
El código actual utiliza la llamada al sistema syslog que funciona como printf.
Ahora preferiría usar un flujo para ese propósito, normalmente el std :: clog incorporado. Pero la obstrucción simplemente redirige la salida a stderr, no a syslog y eso no sirve para mí, ya que también uso stderr y stdout para otros propósitos.
He visto en otra respuesta que es bastante fácil redirigirlo a un archivo usando rdbuf () pero no veo forma de aplicar ese método para llamar a syslog ya que openlog no devuelve un controlador de archivos que pueda usar para vincular una secuencia con él. .
¿Hay otro método para hacer eso? (Parece bastante básico para la programación de Unix)?
Edición: Estoy buscando una solución que no use una biblioteca externa. Lo que propone @Chris podría ser un buen comienzo, pero aún es un poco vago para convertirse en la respuesta aceptada.
Edición : usar Boost.IOStreams está bien ya que mi proyecto ya usa Boost de todos modos.
La vinculación con una biblioteca externa es posible, pero también es una preocupación ya que es un código GPL. Las dependencias también son una carga, ya que pueden entrar en conflicto con otros componentes, no estar disponibles en mi distribución de Linux, introducir errores de terceros, etc. Si esta es la única solución que puedo considerar evitar por completo las transmisiones ... (una pena).
Diseñé una clase OStreamedLog muy similar a la que se muestra arriba, excepto que mis objetos OStreamedLog están configurados para usar un objeto ostringstream arbitrario, como sugirió @Basilevs.
Primero, está la definición de clase de Log, muy similar a lo que @eater y @Chris Kaminski mencionaron anteriormente. Luego, mi definición de clase OStreamedLog, que contiene un objeto Log:
class OStreamedLog : public ostringstream
{
public:
OStreamedLog (const char* ident, int facility)
{
log = new Log (ident, facility);
(static_cast<ostream*>(this))->rdbuf (log);
}
private:
Log* log;
};
Ahora, cuando necesite iniciar sesión, simplemente llame:
OStreamedLog someLog ("MyOwnProgramThatNeedsLogging", LOG_LOCAL1);
someLog << "Log testing" << endl;
someLog << LOG_ERR << "some other error log" << endl;
Por supuesto, podría contraer toda la definición de Registro en su clase OStreamedLog, pero tal vez quiera hacer otras cosas en su objeto de Registro base y usar envoltorios como los anteriores para diferenciar los diferentes tipos de registros. Por ejemplo, podría tener registros de diagnóstico legibles para las personas (enviados como texto ASCII), registros binarios (para su procesamiento posterior) o un registro de transmisión TLS (a un servidor en dirección norte, por ejemplo).
Necesitaba algo simple como esto también, así que simplemente puse esto juntos:
log.h:
enum LogPriority {
kLogEmerg = LOG_EMERG, // system is unusable
kLogAlert = LOG_ALERT, // action must be taken immediately
kLogCrit = LOG_CRIT, // critical conditions
kLogErr = LOG_ERR, // error conditions
kLogWarning = LOG_WARNING, // warning conditions
kLogNotice = LOG_NOTICE, // normal, but significant, condition
kLogInfo = LOG_INFO, // informational message
kLogDebug = LOG_DEBUG // debug-level message
};
std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority);
class Log : public std::basic_streambuf<char, std::char_traits<char> > {
public:
explicit Log(std::string ident, int facility);
protected:
int sync();
int overflow(int c);
private:
friend std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority);
std::string buffer_;
int facility_;
int priority_;
char ident_[50];
};
log.cc:
Log::Log(std::string ident, int facility) {
facility_ = facility;
priority_ = LOG_DEBUG;
strncpy(ident_, ident.c_str(), sizeof(ident_));
ident_[sizeof(ident_)-1] = ''/0'';
openlog(ident_, LOG_PID, facility_);
}
int Log::sync() {
if (buffer_.length()) {
syslog(priority_, buffer_.c_str());
buffer_.erase();
priority_ = LOG_DEBUG; // default to debug for each message
}
return 0;
}
int Log::overflow(int c) {
if (c != EOF) {
buffer_ += static_cast<char>(c);
} else {
sync();
}
return c;
}
std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority) {
static_cast<Log *>(os.rdbuf())->priority_ = (int)log_priority;
return os;
}
En main()
inicializo el zueco:
std::clog.rdbuf(new Log("foo", LOG_LOCAL0));
Entonces, cuando quiera iniciar sesión, es fácil:
std::clog << kLogNotice << "test log message" << std::endl;
std::clog << "the default is debug level" << std::endl;
Otra versión en parte inspirada por el comensal. No redirecciona std :: clog per se, pero usa una sintaxis de flujo familiar.
#ifndef SYSLOG_HPP
#define SYSLOG_HPP
#include <ostream>
#include <streambuf>
#include <string>
#include <syslog.h>
namespace log
{
enum level
{
emergency = LOG_EMERG,
alert = LOG_ALERT,
critical = LOG_CRIT,
error = LOG_ERR,
warning = LOG_WARNING,
notice = LOG_NOTICE,
info = LOG_INFO,
debug = LOG_DEBUG,
};
enum type
{
auth = LOG_AUTH,
cron = LOG_CRON,
daemon = LOG_DAEMON,
local0 = LOG_LOCAL0,
local1 = LOG_LOCAL1,
local2 = LOG_LOCAL2,
local3 = LOG_LOCAL3,
local4 = LOG_LOCAL4,
local5 = LOG_LOCAL5,
local6 = LOG_LOCAL6,
local7 = LOG_LOCAL7,
print = LOG_LPR,
mail = LOG_MAIL,
news = LOG_NEWS,
user = LOG_USER,
uucp = LOG_UUCP,
};
}
class syslog_stream;
class syslog_streambuf: public std::basic_streambuf<char>
{
public:
explicit syslog_streambuf(const std::string& name, log::type type):
std::basic_streambuf<char>()
{
openlog(name.size() ? name.data() : nullptr, LOG_PID, type);
}
~syslog_streambuf() override { closelog(); }
protected:
int_type overflow(int_type c = traits_type::eof()) override
{
if(traits_type::eq_int_type(c, traits_type::eof()))
sync();
else buffer += traits_type::to_char_type(c);
return c;
}
int sync() override
{
if(buffer.size())
{
syslog(level, "%s", buffer.data());
buffer.clear();
level = ini_level;
}
return 0;
}
friend class syslog_stream;
void set_level(log::level new_level) noexcept { level = new_level; }
private:
static constexpr log::level ini_level = log::info;
log::level level = ini_level;
std::string buffer;
};
class syslog_stream: public std::basic_ostream<char>
{
public:
explicit syslog_stream(const std::string& name = std::string(), log::type type = log::user):
std::basic_ostream<char>(&streambuf),
streambuf(name, type)
{ }
syslog_stream& operator<<(log::level level) noexcept
{
streambuf.set_level(level);
return (*this);
}
private:
syslog_streambuf streambuf;
};
#endif // SYSLOG_HPP
Para usarlo, puedes hacer algo como:
syslog_stream clog;
clog << "Hello, world!" << std::endl;
clog << log::emergency << "foo" << "bar" << "baz" << 42 << std::endl;
Podría definir un streambuf que llame a syslog. Por ejemplo:
// Pseudo-code
class syslog_streambuf : public streambuf {
private:
void internal_log(string& log) {
syslog(..., log, ...);
}
public:
int sputc ( char c ) {
internal_log(...);
}
streamsize sputn ( const char * s, streamsize n ) {
internal_log(...);
}
}
entonces simplemente escribirías lo siguiente para redirigir la obstrucción:
clog.rdbuf( new syslog_streambuf );
Hay algunas funciones más que probablemente deberías anular, aquí hay una buena referencia a la API de streambuf .