*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 1410,1416 **** ServerLoop(void)
}
/* If we have lost the log collector, try to start a new one */
! if (SysLoggerPID == 0 && Logging_collector)
SysLoggerPID = SysLogger_Start();
/*
--- 1410,1416 ----
}
/* If we have lost the log collector, try to start a new one */
! if (SysLoggerPID == 0)
SysLoggerPID = SysLogger_Start();
/*
*** a/src/backend/postmaster/syslogger.c
--- b/src/backend/postmaster/syslogger.c
***************
*** 64,73 ****
/*
! * GUC parameters. Logging_collector cannot be changed after postmaster
! * start, but the rest can change at SIGHUP.
*/
- bool Logging_collector = false;
int Log_RotationAge = HOURS_PER_DAY * MINS_PER_HOUR;
int Log_RotationSize = 10 * 1024;
char *Log_directory = NULL;
--- 64,71 ----
/*
! * GUC parameters. All parameters can change at SIGHUP.
*/
int Log_RotationAge = HOURS_PER_DAY * MINS_PER_HOUR;
int Log_RotationSize = 10 * 1024;
char *Log_directory = NULL;
***************
*** 135,140 **** static void syslogger_parseArgs(int argc, char *argv[]);
--- 133,139 ----
static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
static void open_csvlogfile(void);
+ static void write_syslogger_message(char *buffer, int len, bool partial);
#ifdef WIN32
static unsigned int __stdcall pipeThread(void *arg);
***************
*** 454,462 **** SysLogger_Start(void)
pid_t sysloggerPid;
char *filename;
- if (!Logging_collector)
- return 0;
-
/*
* If first time through, create the pipe which will receive stderr
* output.
--- 453,458 ----
***************
*** 777,784 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
* chances and write out a partial message and hope that
* it's not followed by something from another pid.
*/
! write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
! dest);
}
}
else
--- 773,780 ----
* chances and write out a partial message and hope that
* it's not followed by something from another pid.
*/
! write_syslogger_message(cursor + PIPE_HEADER_SIZE, p.len,
! true);
}
}
else
***************
*** 805,819 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
appendBinaryStringInfo(str,
cursor + PIPE_HEADER_SIZE,
p.len);
! write_syslogger_file(str->data, str->len, dest);
saved_chunks[existing_slot].pid = 0;
pfree(str->data);
}
else
{
/* The whole message was one chunk, evidently. */
! write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
! dest);
}
}
--- 801,815 ----
appendBinaryStringInfo(str,
cursor + PIPE_HEADER_SIZE,
p.len);
! write_syslogger_message(str->data, str->len, false);
saved_chunks[existing_slot].pid = 0;
pfree(str->data);
}
else
{
/* The whole message was one chunk, evidently. */
! write_syslogger_message(cursor + PIPE_HEADER_SIZE, p.len,
! false);
}
}
***************
*** 824,829 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
--- 820,826 ----
else
{
/* Process non-protocol data */
+ StringInfo str;
/*
* Look for the start of a protocol header. If found, dump data
***************
*** 839,846 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
if (cursor[chunklen] == '\0')
break;
}
! /* fall back on the stderr log as the destination */
! write_syslogger_file(cursor, chunklen, LOG_DESTINATION_STDERR);
cursor += chunklen;
count -= chunklen;
}
--- 836,853 ----
if (cursor[chunklen] == '\0')
break;
}
!
! /*
! * send non-protocol data through the error log system, so we can add log_line_prefix and such, and
! * get it send to all the required destinatinos.
! */
! str = makeStringInfo();
! appendBinaryStringInfo(str, cursor, chunklen);
! appendStringInfoChar(str, '\0'); /* ensure zero-termination to be sure */
! elog(WARNING, "Non protocol data: %s", str->data);
! pfree(str->data);
! pfree(str);
!
cursor += chunklen;
count -= chunklen;
}
***************
*** 870,876 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
if (saved_chunks[i].pid != 0)
{
str = &(saved_chunks[i].data);
! write_syslogger_file(str->data, str->len, LOG_DESTINATION_STDERR);
saved_chunks[i].pid = 0;
pfree(str->data);
}
--- 877,883 ----
if (saved_chunks[i].pid != 0)
{
str = &(saved_chunks[i].data);
! write_syslogger_message(str->data, str->len, true);
saved_chunks[i].pid = 0;
pfree(str->data);
}
***************
*** 881,888 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
* remove any protocol headers that may exist in it.
*/
if (*bytes_in_logbuffer > 0)
! write_syslogger_file(logbuffer, *bytes_in_logbuffer,
! LOG_DESTINATION_STDERR);
*bytes_in_logbuffer = 0;
}
--- 888,901 ----
* remove any protocol headers that may exist in it.
*/
if (*bytes_in_logbuffer > 0)
! {
! str = makeStringInfo();
! appendBinaryStringInfo(str, logbuffer, *bytes_in_logbuffer);
! appendStringInfoChar(str, '\0');
! elog(WARNING, "Unprocessed log data: %s", str->data);
! pfree(str->data);
! pfree(str);
! }
*bytes_in_logbuffer = 0;
}
***************
*** 894,927 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
/*
* Write text to the currently open logfile
- *
- * This is exported so that elog.c can call it when am_syslogger is true.
- * This allows the syslogger process to record elog messages of its own,
- * even though its stderr does not point at the syslog pipe.
*/
void
! write_syslogger_file(const char *buffer, int count, int destination)
{
- int rc;
- FILE *logfile;
-
- if (destination == LOG_DESTINATION_CSVLOG && csvlogFile == NULL)
- open_csvlogfile();
-
#ifdef WIN32
EnterCriticalSection(&sysfileSection);
#endif
! logfile = destination == LOG_DESTINATION_CSVLOG ? csvlogFile : syslogFile;
! rc = fwrite(buffer, 1, count, logfile);
#ifdef WIN32
LeaveCriticalSection(&sysfileSection);
#endif
/* can't use ereport here because of possible recursion */
if (rc != count)
write_stderr("could not write to log file: %s\n", strerror(errno));
}
#ifdef WIN32
--- 907,988 ----
/*
* Write text to the currently open logfile
*/
void
! write_syslogger_file(const char *buffer)
{
#ifdef WIN32
EnterCriticalSection(&sysfileSection);
#endif
! fwrite(buffer, 1, strlen(buffer), syslogFile);
#ifdef WIN32
LeaveCriticalSection(&sysfileSection);
#endif
/* can't use ereport here because of possible recursion */
+ /* FIXME: need to look at this! */
+ /*
if (rc != count)
write_stderr("could not write to log file: %s\n", strerror(errno));
+ */
+ }
+
+ /*
+ * Write text to the currently open CSV log file
+ */
+ void
+ write_csvlogger_file(const char *buffer)
+ {
+ if (csvlogFile == NULL)
+ open_csvlogfile();
+
+ #ifdef WIN32
+ EnterCriticalSection(&sysfileSection);
+ #endif
+
+ fwrite(buffer, 1, strlen(buffer), csvlogFile);
+
+ #ifdef WIN32
+ LeaveCriticalSection(&sysfileSection);
+ #endif
+
+ /* FIXME: need an error check? */
+ }
+
+
+ void
+ write_syslogger_message(char *buffer, int count, bool partial)
+ {
+ /* Data always starts with a fixed chunk */
+ syslogger_header *header = (syslogger_header *)buffer;
+
+ /* string data */
+ syslogger_strings strings;
+
+ /* pointer to walk through the strings */
+ char *cp = buffer + sizeof(syslogger_header);
+
+ /* FIXME: overflow handling! */
+ #define NEXT_STRING(var) strings.var = cp; cp += strlen(cp)+1; if (strings.var[0] == '\0') strings.var=NULL;
+ NEXT_STRING(user_name);
+ NEXT_STRING(database_name);
+ NEXT_STRING(message);
+ NEXT_STRING(detail);
+ NEXT_STRING(hint);
+ NEXT_STRING(internalquery);
+ NEXT_STRING(context);
+ NEXT_STRING(debug_query_string);
+ NEXT_STRING(funcname);
+ NEXT_STRING(filename);
+
+ /*
+ * Any local destinations (syslog etc) have already received the log message from
+ * the backend process itself. Route the message to any remote destination, which are those
+ * that required the syslogger process in the first place.
+ */
+ route_log_message(header, &strings, Log_destination & LOG_DESTINATION_REMOTE_MASK);
}
#ifdef WIN32
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 146,158 **** pg_rotate_logfile(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to rotate log files"))));
- if (!Logging_collector)
- {
- ereport(WARNING,
- (errmsg("rotation not possible because log collection not active")));
- PG_RETURN_BOOL(false);
- }
-
SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
PG_RETURN_BOOL(true);
}
--- 146,151 ----
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
***************
*** 144,150 **** static char formatted_log_time[FORMATTED_TS_LEN];
} while (0)
! static void log_line_prefix(StringInfo buf, ErrorData *edata);
static void send_message_to_server_log(ErrorData *edata);
static void send_message_to_frontend(ErrorData *edata);
static char *expand_fmt_string(const char *fmt, ErrorData *edata);
--- 144,150 ----
} while (0)
! static void log_line_prefix(StringInfo buf, const syslogger_header *header, const syslogger_strings *str);
static void send_message_to_server_log(ErrorData *edata);
static void send_message_to_frontend(ErrorData *edata);
static char *expand_fmt_string(const char *fmt, ErrorData *edata);
***************
*** 153,161 **** static const char *error_severity(int elevel);
static void append_with_tabs(StringInfo buf, const char *str);
static bool is_log_level_output(int elevel, int log_min_level);
static void write_pipe_chunks(char *data, int len, int dest);
! static void write_csvlog(ErrorData *edata);
! static void setup_formatted_log_time(void);
! static void setup_formatted_start_time(void);
/*
--- 153,161 ----
static void append_with_tabs(StringInfo buf, const char *str);
static bool is_log_level_output(int elevel, int log_min_level);
static void write_pipe_chunks(char *data, int len, int dest);
! static void write_csvlog(const syslogger_header *header, const syslogger_strings *str);
! static void setup_formatted_log_time(const pg_time_t *log_time);
! static void setup_formatted_start_time(const pg_time_t *start_time);
/*
***************
*** 1623,1637 **** write_eventlog(int level, const char *line)
* setup formatted_log_time, for consistent times between CSV and regular logs
*/
static void
! setup_formatted_log_time(void)
{
struct timeval tv;
pg_time_t stamp_time;
pg_tz *tz;
char msbuf[8];
! gettimeofday(&tv, NULL);
! stamp_time = (pg_time_t) tv.tv_sec;
/*
* Normally we print log timestamps in log_timezone, but during startup we
--- 1623,1644 ----
* setup formatted_log_time, for consistent times between CSV and regular logs
*/
static void
! setup_formatted_log_time(const pg_time_t *log_time)
{
struct timeval tv;
pg_time_t stamp_time;
pg_tz *tz;
char msbuf[8];
! if (log_time == NULL || 0==0)
! {
! gettimeofday(&tv, NULL);
! stamp_time = (pg_time_t) tv.tv_sec;
! }
! else
! {
! stamp_time = *log_time;
! }
/*
* Normally we print log timestamps in log_timezone, but during startup we
***************
*** 1655,1665 **** setup_formatted_log_time(void)
* setup formatted_start_time
*/
static void
! setup_formatted_start_time(void)
{
! pg_time_t stamp_time = (pg_time_t) MyStartTime;
pg_tz *tz;
/*
* Normally we print log timestamps in log_timezone, but during startup we
* could get here before that's set. If so, fall back to gmt_timezone
--- 1662,1676 ----
* setup formatted_start_time
*/
static void
! setup_formatted_start_time(const pg_time_t *start_time)
{
! pg_time_t stamp_time;
pg_tz *tz;
+ if (start_time == NULL)
+ stamp_time = (pg_time_t) MyStartTime;
+ else
+ stamp_time = *start_time;
/*
* Normally we print log timestamps in log_timezone, but during startup we
* could get here before that's set. If so, fall back to gmt_timezone
***************
*** 1677,1683 **** setup_formatted_start_time(void)
* Format tag info for log lines; append to the provided buffer.
*/
static void
! log_line_prefix(StringInfo buf, ErrorData *edata)
{
/* static counter for line numbers */
static long log_line_number = 0;
--- 1688,1694 ----
* Format tag info for log lines; append to the provided buffer.
*/
static void
! log_line_prefix(StringInfo buf, const syslogger_header *header, const syslogger_strings *str)
{
/* static counter for line numbers */
static long log_line_number = 0;
***************
*** 1724,1764 **** log_line_prefix(StringInfo buf, ErrorData *edata)
switch (Log_line_prefix[i])
{
case 'u':
! if (MyProcPort)
! {
! const char *username = MyProcPort->user_name;
!
! if (username == NULL || *username == '\0')
! username = _("[unknown]");
! appendStringInfo(buf, "%s", username);
! }
break;
case 'd':
! if (MyProcPort)
! {
! const char *dbname = MyProcPort->database_name;
!
! if (dbname == NULL || *dbname == '\0')
! dbname = _("[unknown]");
! appendStringInfo(buf, "%s", dbname);
! }
break;
case 'c':
! appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid);
break;
case 'p':
! appendStringInfo(buf, "%d", MyProcPid);
break;
case 'l':
! appendStringInfo(buf, "%ld", log_line_number);
break;
case 'm':
! setup_formatted_log_time();
appendStringInfoString(buf, formatted_log_time);
break;
case 't':
{
! pg_time_t stamp_time = (pg_time_t) time(NULL);
pg_tz *tz;
char strfbuf[128];
--- 1735,1761 ----
switch (Log_line_prefix[i])
{
case 'u':
! appendStringInfo(buf, "%s", str->user_name?str->user_name:"[unknown]");
break;
case 'd':
! appendStringInfo(buf, "%s", str->database_name?str->database_name:"[unknown]");
break;
case 'c':
! appendStringInfo(buf, "%lx.%x", (long)header->starttime, header->pid);
break;
case 'p':
! appendStringInfo(buf, "%d", header->pid);
break;
case 'l':
! appendStringInfo(buf, "%ld", header->linenum);
break;
case 'm':
! setup_formatted_log_time(&header->timestamp);
appendStringInfoString(buf, formatted_log_time);
break;
case 't':
{
! pg_time_t stamp_time = header->timestamp;
pg_tz *tz;
char strfbuf[128];
***************
*** 1772,1781 **** log_line_prefix(StringInfo buf, ErrorData *edata)
break;
case 's':
if (formatted_start_time[0] == '\0')
! setup_formatted_start_time();
appendStringInfoString(buf, formatted_start_time);
break;
case 'i':
if (MyProcPort)
{
const char *psdisp;
--- 1769,1779 ----
break;
case 's':
if (formatted_start_time[0] == '\0')
! setup_formatted_start_time(&header->starttime);
appendStringInfoString(buf, formatted_start_time);
break;
case 'i':
+ /* FIXME */
if (MyProcPort)
{
const char *psdisp;
***************
*** 1786,1821 **** log_line_prefix(StringInfo buf, ErrorData *edata)
}
break;
case 'r':
! if (MyProcPort && MyProcPort->remote_host)
{
! appendStringInfo(buf, "%s", MyProcPort->remote_host);
! if (MyProcPort->remote_port &&
! MyProcPort->remote_port[0] != '\0')
appendStringInfo(buf, "(%s)",
! MyProcPort->remote_port);
}
break;
case 'h':
! if (MyProcPort && MyProcPort->remote_host)
! appendStringInfo(buf, "%s", MyProcPort->remote_host);
break;
case 'q':
/* in postmaster and friends, stop if %q is seen */
/* in a backend, just ignore */
! if (MyProcPort == NULL)
i = format_len;
break;
case 'v':
/* keep VXID format in sync with lockfuncs.c */
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
appendStringInfo(buf, "%d/%u",
MyProc->backendId, MyProc->lxid);
break;
case 'x':
! appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
break;
case 'e':
! appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
break;
case '%':
appendStringInfoChar(buf, '%');
--- 1784,1820 ----
}
break;
case 'r':
! if (str->remote_host != NULL)
{
! appendStringInfo(buf, "%s", str->remote_host);
! if (str->remote_port &&
! str->remote_port[0] != '\0')
appendStringInfo(buf, "(%s)",
! str->remote_port);
}
break;
case 'h':
! if (str->remote_host)
! appendStringInfo(buf, "%s", str->remote_host);
break;
case 'q':
/* in postmaster and friends, stop if %q is seen */
/* in a backend, just ignore */
! if (str->remote_host == NULL)
i = format_len;
break;
case 'v':
+ /* FIXME */
/* keep VXID format in sync with lockfuncs.c */
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
appendStringInfo(buf, "%d/%u",
MyProc->backendId, MyProc->lxid);
break;
case 'x':
! appendStringInfo(buf, "%u", header->xid);
break;
case 'e':
! appendStringInfoString(buf, unpack_sql_state(header->sqlerrcode));
break;
case '%':
appendStringInfoChar(buf, '%');
***************
*** 1857,1863 **** appendCSVLiteral(StringInfo buf, const char *data)
* format which is described in doc/src/sgml/config.sgml.
*/
static void
! write_csvlog(ErrorData *edata)
{
StringInfoData buf;
bool print_stmt = false;
--- 1856,1862 ----
* format which is described in doc/src/sgml/config.sgml.
*/
static void
! write_csvlog(const syslogger_header *header, const syslogger_strings *str)
{
StringInfoData buf;
bool print_stmt = false;
***************
*** 1891,1917 **** write_csvlog(ErrorData *edata)
* to put same timestamp in both syslog and csvlog messages.
*/
if (formatted_log_time[0] == '\0')
! setup_formatted_log_time();
appendStringInfoString(&buf, formatted_log_time);
appendStringInfoChar(&buf, ',');
/* username */
! if (MyProcPort)
! appendCSVLiteral(&buf, MyProcPort->user_name);
appendStringInfoChar(&buf, ',');
/* database name */
! if (MyProcPort)
! appendCSVLiteral(&buf, MyProcPort->database_name);
appendStringInfoChar(&buf, ',');
/* Process id */
! if (MyProcPid != 0)
! appendStringInfo(&buf, "%d", MyProcPid);
appendStringInfoChar(&buf, ',');
/* Remote host and port */
if (MyProcPort && MyProcPort->remote_host)
{
appendStringInfoChar(&buf, '"');
--- 1890,1914 ----
* to put same timestamp in both syslog and csvlog messages.
*/
if (formatted_log_time[0] == '\0')
! setup_formatted_log_time(&header->timestamp);
appendStringInfoString(&buf, formatted_log_time);
appendStringInfoChar(&buf, ',');
/* username */
! appendCSVLiteral(&buf, str->user_name);
appendStringInfoChar(&buf, ',');
/* database name */
! appendCSVLiteral(&buf, str->database_name);
appendStringInfoChar(&buf, ',');
/* Process id */
! appendStringInfo(&buf, "%d", header->pid);
appendStringInfoChar(&buf, ',');
/* Remote host and port */
+ /* FIXME
if (MyProcPort && MyProcPort->remote_host)
{
appendStringInfoChar(&buf, '"');
***************
*** 1921,1929 **** write_csvlog(ErrorData *edata)
appendStringInfoChar(&buf, '"');
}
appendStringInfoChar(&buf, ',');
/* session id */
! appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
appendStringInfoChar(&buf, ',');
/* Line number */
--- 1918,1927 ----
appendStringInfoChar(&buf, '"');
}
appendStringInfoChar(&buf, ',');
+ */
/* session id */
! appendStringInfo(&buf, "%lx.%x", (long) header->starttime, header->pid);
appendStringInfoChar(&buf, ',');
/* Line number */
***************
*** 1931,1936 **** write_csvlog(ErrorData *edata)
--- 1929,1935 ----
appendStringInfoChar(&buf, ',');
/* PS display */
+ /* FIXME
if (MyProcPort)
{
StringInfoData msgbuf;
***************
*** 1946,2014 **** write_csvlog(ErrorData *edata)
pfree(msgbuf.data);
}
appendStringInfoChar(&buf, ',');
/* session start timestamp */
if (formatted_start_time[0] == '\0')
! setup_formatted_start_time();
appendStringInfoString(&buf, formatted_start_time);
appendStringInfoChar(&buf, ',');
/* Virtual transaction id */
/* keep VXID format in sync with lockfuncs.c */
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
appendStringInfoChar(&buf, ',');
/* Transaction id */
appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
appendStringInfoChar(&buf, ',');
/* Error severity */
! appendStringInfo(&buf, "%s", error_severity(edata->elevel));
appendStringInfoChar(&buf, ',');
/* SQL state code */
! appendStringInfo(&buf, "%s", unpack_sql_state(edata->sqlerrcode));
appendStringInfoChar(&buf, ',');
/* errmessage */
! appendCSVLiteral(&buf, edata->message);
appendStringInfoCharMacro(&buf, ',');
/* errdetail or errdetail_log */
! if (edata->detail_log)
! appendCSVLiteral(&buf, edata->detail_log);
! else
! appendCSVLiteral(&buf, edata->detail);
appendStringInfoCharMacro(&buf, ',');
/* errhint */
! appendCSVLiteral(&buf, edata->hint);
appendStringInfoCharMacro(&buf, ',');
/* internal query */
! appendCSVLiteral(&buf, edata->internalquery);
appendStringInfoCharMacro(&buf, ',');
/* if printed internal query, print internal pos too */
! if (edata->internalpos > 0 && edata->internalquery != NULL)
! appendStringInfo(&buf, "%d", edata->internalpos);
appendStringInfoCharMacro(&buf, ',');
/* errcontext */
! appendCSVLiteral(&buf, edata->context);
appendStringInfoCharMacro(&buf, ',');
/* user query --- only reported if not disabled by the caller */
! if (is_log_level_output(edata->elevel, log_min_error_statement) &&
! debug_query_string != NULL &&
! !edata->hide_stmt)
print_stmt = true;
if (print_stmt)
! appendCSVLiteral(&buf, debug_query_string);
appendStringInfoCharMacro(&buf, ',');
! if (print_stmt && edata->cursorpos > 0)
! appendStringInfo(&buf, "%d", edata->cursorpos);
appendStringInfoCharMacro(&buf, ',');
/* file error location */
--- 1945,2014 ----
pfree(msgbuf.data);
}
appendStringInfoChar(&buf, ',');
+ */
/* session start timestamp */
if (formatted_start_time[0] == '\0')
! setup_formatted_start_time(&header->starttime);
appendStringInfoString(&buf, formatted_start_time);
appendStringInfoChar(&buf, ',');
/* Virtual transaction id */
/* keep VXID format in sync with lockfuncs.c */
+ /* FIXME:
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
appendStringInfoChar(&buf, ',');
+ */
/* Transaction id */
+ /* FIXME
appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
appendStringInfoChar(&buf, ',');
+ */
/* Error severity */
! appendStringInfo(&buf, "%s", error_severity(header->elevel));
appendStringInfoChar(&buf, ',');
/* SQL state code */
! appendStringInfo(&buf, "%s", unpack_sql_state(header->sqlerrcode));
appendStringInfoChar(&buf, ',');
/* errmessage */
! appendCSVLiteral(&buf, str->message);
appendStringInfoCharMacro(&buf, ',');
/* errdetail or errdetail_log */
! appendCSVLiteral(&buf, str->detail);
appendStringInfoCharMacro(&buf, ',');
/* errhint */
! appendCSVLiteral(&buf, str->hint);
appendStringInfoCharMacro(&buf, ',');
/* internal query */
! appendCSVLiteral(&buf, str->internalquery);
appendStringInfoCharMacro(&buf, ',');
/* if printed internal query, print internal pos too */
! if (header->internalpos > 0 && str->internalquery != NULL)
! appendStringInfo(&buf, "%d", header->internalpos);
appendStringInfoCharMacro(&buf, ',');
/* errcontext */
! appendCSVLiteral(&buf, str->context);
appendStringInfoCharMacro(&buf, ',');
/* user query --- only reported if not disabled by the caller */
! if (is_log_level_output(header->elevel, header->log_min_error_statement) &&
! str->debug_query_string != NULL)
print_stmt = true;
if (print_stmt)
! appendCSVLiteral(&buf, str->debug_query_string);
appendStringInfoCharMacro(&buf, ',');
! if (print_stmt && header->cursorpos > 0)
! appendStringInfo(&buf, "%d", header->cursorpos);
appendStringInfoCharMacro(&buf, ',');
/* file error location */
***************
*** 2018,2041 **** write_csvlog(ErrorData *edata)
initStringInfo(&msgbuf);
! if (edata->funcname && edata->filename)
appendStringInfo(&msgbuf, "%s, %s:%d",
! edata->funcname, edata->filename,
! edata->lineno);
! else if (edata->filename)
appendStringInfo(&msgbuf, "%s:%d",
! edata->filename, edata->lineno);
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
}
appendStringInfoChar(&buf, '\n');
! /* If in the syslogger process, try to write messages direct to file */
! if (am_syslogger)
! write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
! else
! write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
pfree(buf.data);
}
--- 2018,2037 ----
initStringInfo(&msgbuf);
! if (str->funcname && str->filename)
appendStringInfo(&msgbuf, "%s, %s:%d",
! str->funcname, str->filename,
! header->error_lineno);
! else if (str->filename)
appendStringInfo(&msgbuf, "%s:%d",
! str->filename, header->error_lineno);
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
}
appendStringInfoChar(&buf, '\n');
! write_csvlogger_file(buf.data);
pfree(buf.data);
}
***************
*** 2062,2150 **** unpack_sql_state(int sql_state)
/*
! * Write error report to server's log
*/
static void
send_message_to_server_log(ErrorData *edata)
{
StringInfoData buf;
! initStringInfo(&buf);
! formatted_log_time[0] = '\0';
! log_line_prefix(&buf, edata);
! appendStringInfo(&buf, "%s: ", error_severity(edata->elevel));
if (Log_error_verbosity >= PGERROR_VERBOSE)
! appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));
! if (edata->message)
! append_with_tabs(&buf, edata->message);
else
! append_with_tabs(&buf, _("missing error text"));
! if (edata->cursorpos > 0)
! appendStringInfo(&buf, _(" at character %d"),
! edata->cursorpos);
! else if (edata->internalpos > 0)
! appendStringInfo(&buf, _(" at character %d"),
! edata->internalpos);
! appendStringInfoChar(&buf, '\n');
if (Log_error_verbosity >= PGERROR_DEFAULT)
{
! if (edata->detail_log)
{
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("DETAIL: "));
! append_with_tabs(&buf, edata->detail_log);
! appendStringInfoChar(&buf, '\n');
}
! else if (edata->detail)
{
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("DETAIL: "));
! append_with_tabs(&buf, edata->detail);
! appendStringInfoChar(&buf, '\n');
}
! if (edata->hint)
! {
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("HINT: "));
! append_with_tabs(&buf, edata->hint);
! appendStringInfoChar(&buf, '\n');
! }
! if (edata->internalquery)
{
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("QUERY: "));
! append_with_tabs(&buf, edata->internalquery);
! appendStringInfoChar(&buf, '\n');
}
! if (edata->context)
{
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("CONTEXT: "));
! append_with_tabs(&buf, edata->context);
! appendStringInfoChar(&buf, '\n');
}
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
/* assume no newlines in funcname or filename... */
! if (edata->funcname && edata->filename)
{
! log_line_prefix(&buf, edata);
! appendStringInfo(&buf, _("LOCATION: %s, %s:%d\n"),
! edata->funcname, edata->filename,
! edata->lineno);
}
! else if (edata->filename)
{
! log_line_prefix(&buf, edata);
! appendStringInfo(&buf, _("LOCATION: %s:%d\n"),
! edata->filename, edata->lineno);
}
}
}
--- 2058,2281 ----
/*
! * Write error report to pipe to syslogger
*/
+ #define EMPTY_STRING(x) ((x)?(x):"")
+ #define appendStringInfoVariable(n) do { appendStringInfoString(&buf, EMPTY_STRING(str.n));appendStringInfoChar(&buf,'\0'); } while (0);
static void
send_message_to_server_log(ErrorData *edata)
{
StringInfoData buf;
+ struct timeval tv;
! /* static counter for line numbers */
! static long log_line_number = 0;
!
! /* has counter been reset in current process? */
! static int log_my_pid = 0;
!
! /* header with fixed-size data to send */
! syslogger_header header;
! /* variable length strings */
! syslogger_strings str;
!
! /*
! * This is one of the few places where we'd rather not inherit a static
! * variable's value from the postmaster. But since we will, reset it when
! * MyProcPid changes.
! */
! if (log_my_pid != MyProcPid)
! {
! log_line_number = 0;
! log_my_pid = MyProcPid;
! }
! log_line_number++;
! /* Get the current time for logging */
! gettimeofday(&tv, NULL);
! /* Set up all header values */
! memset(&header, 0, sizeof(header));
!
! header.pid = MyProcPid;
! header.timestamp = tv.tv_sec;
! header.starttime = MyStartTime;
! header.linenum = log_line_number;
! if (MyProc)
! {
! header.backendid = MyProc->backendId;
! header.lxid = MyProc->lxid;
! }
! header.xid = GetTopTransactionIdIfAny();
! header.elevel = edata->elevel;
! header.sqlerrcode = edata->sqlerrcode;
! header.internalpos = edata->internalpos;
! header.log_min_error_statement = log_min_error_statement;
! header.cursorpos = edata->cursorpos;
! header.error_lineno = edata->lineno;
!
! /* Set up strings to be sent */
! str.user_name = MyProcPort ? MyProcPort->user_name : "";
! str.database_name = MyProcPort ? MyProcPort->database_name : "";
! str.message = edata->message;
! str.detail = edata->detail_log ? edata->detail_log : edata->detail;
! str.hint = edata->hint;
! str.internalquery = edata->internalquery;
! str.context = edata->context;
! str.debug_query_string = edata->hide_stmt ? "" : debug_query_string;
! str.funcname = edata->funcname;
! str.filename = edata->filename;
! if (MyProcPort) {
! str.remote_host = MyProcPort->remote_host;
! if (MyProcPort->remote_port)
! str.remote_port = MyProcPort->remote_host;
! else
! str.remote_port = NULL;
! }
! else {
! str.remote_host = NULL;
! str.remote_port = NULL;
! }
!
! if (am_syslogger)
! {
! /*
! * Running in syslogger process, write data directly to the destination
! */
! route_log_message(&header, &str, Log_destination);
! }
! else
! {
! if (!redirection_done)
! {
! /*
! * If redirection hasn't been done yet, there is no way we can write any data
! * to the syslogger, so just skip sending to remote.
! * If any local destinations are configured, send there.
! * If there are no local destinations configured, send to stderr anyway,
! * so we don't loose the data.
! */
! if (Log_destination & LOG_DESTINATION_LOCAL_MASK)
! {
! route_log_message(&header, &str, Log_destination & LOG_DESTINATION_LOCAL_MASK);
! }
! else
! {
! route_log_message(&header, &str, LOG_DESTINATION_STDERR);
! }
! }
! else
! {
! /* Redirection has been done */
! if (Log_destination & LOG_DESTINATION_LOCAL_MASK)
! {
! /*
! * Log destination contains at least one destination that we write directly to,
! * bypassing the syslogger process.
! */
! route_log_message(&header, &str, Log_destination & LOG_DESTINATION_LOCAL_MASK);
! }
!
! if (Log_destination & LOG_DESTINATION_REMOTE_MASK)
! {
! /*
! * If our log destination contains any destination that requires the syslogger,
! * send the data there.
! */
!
! initStringInfo(&buf);
! appendBinaryStringInfo(&buf, (const char *)&header, sizeof(header));
! appendStringInfoVariable(user_name);
! appendStringInfoVariable(database_name);
! appendStringInfoVariable(message);
! appendStringInfoVariable(detail);
! appendStringInfoVariable(hint);
! appendStringInfoVariable(internalquery);
! appendStringInfoVariable(context);
! appendStringInfoVariable(debug_query_string);
! appendStringInfoVariable(funcname);
! appendStringInfoVariable(filename);
! appendStringInfoVariable(remote_host);
! appendStringInfoVariable(remote_port);
!
! /* Send the data off to the syslogger */
! write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
! }
! }
! }
! }
!
! /*
! * Format a log line
! */
! void
! format_log_line(StringInfoData *buf, const syslogger_header *header, const syslogger_strings *str)
! {
! log_line_prefix(buf, header, str);
! appendStringInfo(buf, "%s: ", error_severity(header->elevel));
if (Log_error_verbosity >= PGERROR_VERBOSE)
! appendStringInfo(buf, "%s: ", unpack_sql_state(header->sqlerrcode));
! if (str->message)
! append_with_tabs(buf, str->message);
else
! append_with_tabs(buf, _("missing error text"));
! if (header->cursorpos > 0)
! appendStringInfo(buf, _(" at character %d"),
! header->cursorpos);
! else if (header->internalpos > 0)
! appendStringInfo(buf, _(" at character %d"),
! header->internalpos);
! appendStringInfoChar(buf, '\n');
if (Log_error_verbosity >= PGERROR_DEFAULT)
{
! if (str->detail)
{
! log_line_prefix(buf, header, str);
! appendStringInfoString(buf, _("DETAIL: "));
! append_with_tabs(buf, str->detail);
! appendStringInfoChar(buf, '\n');
}
! if (str->hint)
{
! log_line_prefix(buf, header, str);
! appendStringInfoString(buf, _("HINT: "));
! append_with_tabs(buf, str->hint);
! appendStringInfoChar(buf, '\n');
}
! if (str->internalquery)
{
! log_line_prefix(buf, header, str);
! appendStringInfoString(buf, _("QUERY: "));
! append_with_tabs(buf, str->internalquery);
! appendStringInfoChar(buf, '\n');
}
! if (str->context)
{
! log_line_prefix(buf, header, str);
! appendStringInfoString(buf, _("CONTEXT: "));
! append_with_tabs(buf, str->context);
! appendStringInfoChar(buf, '\n');
}
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
/* assume no newlines in funcname or filename... */
! if (str->funcname && str->filename)
{
! log_line_prefix(buf, header, str);
! appendStringInfo(buf, _("LOCATION: %s, %s:%d\n"),
! str->funcname, str->filename,
! header->error_lineno);
}
! else if (str->filename)
{
! log_line_prefix(buf, header, str);
! appendStringInfo(buf, _("LOCATION: %s:%d\n"),
! str->filename, header->error_lineno);
}
}
}
***************
*** 2152,2174 **** send_message_to_server_log(ErrorData *edata)
/*
* If the user wants the query that generated this error logged, do it.
*/
! if (is_log_level_output(edata->elevel, log_min_error_statement) &&
! debug_query_string != NULL &&
! !edata->hide_stmt)
{
! log_line_prefix(&buf, edata);
! appendStringInfoString(&buf, _("STATEMENT: "));
! append_with_tabs(&buf, debug_query_string);
! appendStringInfoChar(&buf, '\n');
}
#ifdef HAVE_SYSLOG
/* Write to syslog, if enabled */
! if (Log_destination & LOG_DESTINATION_SYSLOG)
{
int syslog_level;
! switch (edata->elevel)
{
case DEBUG5:
case DEBUG4:
--- 2283,2328 ----
/*
* If the user wants the query that generated this error logged, do it.
*/
! if (is_log_level_output(header->elevel, header->log_min_error_statement) &&
! str->debug_query_string != NULL)
{
! log_line_prefix(buf, header, str);
! appendStringInfoString(buf, _("STATEMENT: "));
! append_with_tabs(buf, str->debug_query_string);
! appendStringInfoChar(buf, '\n');
}
+ }
+
+ /*
+ * Route the log message to the proper destination. This can either be a local destination
+ * (syslog, stderr etc), or a remote one (the ones that require the syslogger process, such
+ * as logfile).
+ * For remote destinations, this function is only called inside the syslogger, so we write
+ * directly to the destination in this case as well.
+ */
+ void
+ route_log_message(const syslogger_header *header, const syslogger_strings *strings, int destinations)
+ {
+ StringInfoData buf;
+
+ /*
+ * If our destination needs a general format string, set it up here
+ */
+ if (destinations & (LOG_DESTINATION_SYSLOG | LOG_DESTINATION_EVENTLOG | LOG_DESTINATION_FILE | LOG_DESTINATION_STDERR))
+ {
+ initStringInfo(&buf);
+ format_log_line(&buf, header, strings);
+ }
+ else
+ buf.data = NULL;
#ifdef HAVE_SYSLOG
/* Write to syslog, if enabled */
! if (destinations & LOG_DESTINATION_SYSLOG)
{
int syslog_level;
! switch (header->elevel)
{
case DEBUG5:
case DEBUG4:
***************
*** 2204,2275 **** send_message_to_server_log(ErrorData *edata)
#ifdef WIN32
/* Write to eventlog, if enabled */
! if (Log_destination & LOG_DESTINATION_EVENTLOG)
{
! write_eventlog(edata->elevel, buf.data);
}
#endif /* WIN32 */
! /* Write to stderr, if enabled */
! if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == DestDebug)
{
! /*
! * Use the chunking protocol if we know the syslogger should be
! * catching stderr output, and we are not ourselves the syslogger.
! * Otherwise, just do a vanilla write to stderr.
! */
! if (redirection_done && !am_syslogger)
! write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
! #ifdef WIN32
!
! /*
! * In a win32 service environment, there is no usable stderr. Capture
! * anything going there and write it to the eventlog instead.
! *
! * If stderr redirection is active, it was OK to write to stderr above
! * because that's really a pipe to the syslogger process.
! */
! else if (pgwin32_is_service())
! write_eventlog(edata->elevel, buf.data);
! #endif
! else
! write(fileno(stderr), buf.data, buf.len);
}
! /* If in the syslogger process, try to write messages direct to file */
! if (am_syslogger)
! write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR);
/* Write to CSV log if enabled */
! if (Log_destination & LOG_DESTINATION_CSVLOG)
{
! if (redirection_done || am_syslogger)
! {
! /*
! * send CSV data if it's safe to do so (syslogger doesn't need the
! * pipe). First get back the space in the message buffer.
! */
! pfree(buf.data);
! write_csvlog(edata);
! }
! else
! {
! const char *msg = _("Not safe to send CSV data\n");
!
! write(fileno(stderr), msg, strlen(msg));
! if (!(Log_destination & LOG_DESTINATION_STDERR) &&
! whereToSendOutput != DestDebug)
! {
! /* write message to stderr unless we just sent it above */
! write(fileno(stderr), buf.data, buf.len);
! }
! pfree(buf.data);
! }
}
! else
! {
pfree(buf.data);
- }
}
/*
--- 2358,2396 ----
#ifdef WIN32
/* Write to eventlog, if enabled */
! if (destinations & LOG_DESTINATION_EVENTLOG)
{
! write_eventlog(header->elevel, buf.data);
}
#endif /* WIN32 */
! /*
! * Write to stderr if enabled. We are called with this in two cases:
! * - in the syslogger, when configured to write to stderr
! * - in a backend, if the syslogger is not (yet) running. In this case,
! * just dumping the data right out on our local stderr is the best
! * thing we can do.
! */
! if (destinations & LOG_DESTINATION_STDERR)
{
! /* FIXME: don't ignore write()'s return value */
! write(fileno(stderr), buf.data, buf.len);
}
! /* Write to syslogger file, if enabled */
! if (destinations & LOG_DESTINATION_FILE)
! {
! write_syslogger_file(buf.data);
! }
/* Write to CSV log if enabled */
! if (destinations & LOG_DESTINATION_CSVLOG)
{
! write_csvlog(header, strings);
}
!
! if (buf.data)
pfree(buf.data);
}
/*
***************
*** 2293,2298 **** write_pipe_chunks(char *data, int len, int dest)
--- 2414,2420 ----
p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
p.proto.len = PIPE_MAX_PAYLOAD;
memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
+ /* FIXME: don't ignore write()'s return value */
write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
data += PIPE_MAX_PAYLOAD;
len -= PIPE_MAX_PAYLOAD;
***************
*** 2302,2307 **** write_pipe_chunks(char *data, int len, int dest)
--- 2424,2430 ----
p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
p.proto.len = len;
memcpy(p.proto.data, data, len);
+ /* FIXME: don't ignore write()'s return value */
write(fd, &p, PIPE_HEADER_SIZE + len);
}
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1089,1102 **** static struct config_bool ConfigureNamesBool[] =
false, NULL, NULL
},
{
- {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
- gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."),
- NULL
- },
- &Logging_collector,
- false, NULL, NULL
- },
- {
{"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Truncate existing log files of same name during log rotation."),
NULL
--- 1089,1094 ----
***************
*** 7235,7240 **** assign_log_destination(const char *value, bool doit, GucSource source)
--- 7227,7234 ----
if (pg_strcasecmp(tok, "stderr") == 0)
newlogdest |= LOG_DESTINATION_STDERR;
+ else if (pg_strcasecmp(tok, "file") == 0)
+ newlogdest |= LOG_DESTINATION_FILE;
else if (pg_strcasecmp(tok, "csvlog") == 0)
newlogdest |= LOG_DESTINATION_CSVLOG;
#ifdef HAVE_SYSLOG
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 234,250 ****
# - Where to Log -
#log_destination = 'stderr' # Valid values are combinations of
! # stderr, csvlog, syslog and eventlog,
! # depending on platform. csvlog
! # requires logging_collector to be on.
!
! # This is used when logging to stderr:
! #logging_collector = off # Enable capturing of stderr and csvlog
! # into log files. Required to be on for
! # csvlogs.
! # (change requires restart)
! # These are only used if logging_collector is on:
#log_directory = 'pg_log' # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
--- 234,243 ----
# - Where to Log -
#log_destination = 'stderr' # Valid values are combinations of
! # stderr, file, csvlog, syslog and eventlog,
! # depending on platform.
! # These are only used for file and csvlog destinations.
#log_directory = 'pg_log' # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
*** a/src/include/postmaster/syslogger.h
--- b/src/include/postmaster/syslogger.h
***************
*** 60,68 **** typedef union
#define PIPE_HEADER_SIZE offsetof(PipeProtoHeader, data)
#define PIPE_MAX_PAYLOAD ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
/* GUC options */
- extern bool Logging_collector;
extern int Log_RotationAge;
extern int Log_RotationSize;
extern PGDLLIMPORT char *Log_directory;
--- 60,102 ----
#define PIPE_HEADER_SIZE offsetof(PipeProtoHeader, data)
#define PIPE_MAX_PAYLOAD ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
+ /*
+ * header struct sent over pipe to syslogger
+ */
+ typedef struct
+ {
+ int pid;
+ pg_time_t timestamp;
+ pg_time_t starttime;
+ long int linenum;
+ int backendid;
+ LocalTransactionId lxid;
+ TransactionId xid;
+ int elevel;
+ int sqlerrcode;
+ int internalpos;
+ int log_min_error_statement;
+ int cursorpos;
+ int error_lineno;
+ } syslogger_header;
+
+ typedef struct
+ {
+ const char *user_name;
+ const char *database_name;
+ const char *message;
+ const char *detail;
+ const char *hint;
+ const char *internalquery;
+ const char *context;
+ const char *debug_query_string;
+ const char *funcname;
+ const char *filename;
+ const char *remote_host;
+ const char *remote_port;
+ } syslogger_strings;
/* GUC options */
extern int Log_RotationAge;
extern int Log_RotationSize;
extern PGDLLIMPORT char *Log_directory;
***************
*** 80,89 **** extern HANDLE syslogPipe[2];
extern int SysLogger_Start(void);
! extern void write_syslogger_file(const char *buffer, int count, int dest);
#ifdef EXEC_BACKEND
extern void SysLoggerMain(int argc, char *argv[]);
#endif
#endif /* _SYSLOGGER_H */
--- 114,128 ----
extern int SysLogger_Start(void);
! extern void route_log_message(const syslogger_header *header, const syslogger_strings *strings, int destinations);
! extern void write_syslogger_file(const char *buffer);
! extern void write_csvlogger_file(const char *buffer);
#ifdef EXEC_BACKEND
extern void SysLoggerMain(int argc, char *argv[]);
#endif
+ /* this is in elog.c, and probably shouldn't be! */
+ void format_log_line(StringInfoData *buf, const syslogger_header *header, const syslogger_strings *str);
+
#endif /* _SYSLOGGER_H */
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 337,342 **** extern int Log_destination;
--- 337,347 ----
#define LOG_DESTINATION_SYSLOG 2
#define LOG_DESTINATION_EVENTLOG 4
#define LOG_DESTINATION_CSVLOG 8
+ #define LOG_DESTINATION_FILE 16
+
+ /* Remote (=via syslogger) and local (directly from backend) destination masks */
+ #define LOG_DESTINATION_LOCAL_MASK (LOG_DESTINATION_SYSLOG | LOG_DESTINATION_EVENTLOG)
+ #define LOG_DESTINATION_REMOTE_MASK (~(LOG_DESTINATION_LOCAL_MASK))
/* Other exported functions */
extern void DebugFileOpen(void);