@ -182,6 +182,7 @@ static bool matches_backtrace_functions(const char *funcname);
static pg_noinline void set_backtrace ( ErrorData * edata , int num_skip ) ;
static void set_errdata_field ( MemoryContextData * cxt , char * * ptr , const char * str ) ;
static void FreeErrorDataContents ( ErrorData * edata ) ;
static int log_min_messages_cmp ( const ListCell * a , const ListCell * b ) ;
static void write_console ( const char * line , int len ) ;
static const char * process_log_prefix_padding ( const char * p , int * ppadding ) ;
static void log_line_prefix ( StringInfo buf , ErrorData * edata ) ;
@ -235,7 +236,7 @@ is_log_level_output(int elevel, int log_min_level)
static inline bool
should_output_to_server ( int elevel )
{
return is_log_level_output ( elevel , log_min_messages ) ;
return is_log_level_output ( elevel , log_min_messages [ MyBackendType ] ) ;
}
/*
@ -2170,6 +2171,250 @@ DebugFileOpen(void)
}
/*
* GUC check_hook for log_min_messages
*
* This value is parsed as a comma - separated list of zero or more TYPE : LEVEL
* elements . For each element , TYPE corresponds to a bk_category value ( see
* postmaster / proctypelist . h ) ; LEVEL is one of server_message_level_options .
*
* In addition , there must be a single LEVEL element ( with no TYPE part )
* which sets the default level for process types that aren ' t specified .
*/
bool
check_log_min_messages ( char * * newval , void * * extra , GucSource source )
{
char * rawstring ;
List * elemlist ;
StringInfoData buf ;
char * result ;
int newlevel [ BACKEND_NUM_TYPES ] ;
bool assigned [ BACKEND_NUM_TYPES ] = { 0 } ;
int genericlevel = - 1 ; /* -1 means not assigned */
const char * const process_types [ ] = {
# define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
[ bktype ] = bkcategory ,
# include "postmaster/proctypelist.h"
# undef PG_PROCTYPE
} ;
/* Need a modifiable copy of string. */
rawstring = guc_strdup ( LOG , * newval ) ;
if ( rawstring = = NULL )
return false ;
/* Parse the string into a list. */
if ( ! SplitGUCList ( rawstring , ' , ' , & elemlist ) )
{
/* syntax error in list */
GUC_check_errdetail ( " List syntax is invalid. " ) ;
list_free ( elemlist ) ;
guc_free ( rawstring ) ;
return false ;
}
/* Validate and assign log level and process type. */
foreach_ptr ( char , elem , elemlist )
{
char * sep = strchr ( elem , ' : ' ) ;
/*
* If there ' s no ' : ' separator in the entry , this is the default log
* level . Otherwise it ' s a process type - specific entry .
*/
if ( sep = = NULL )
{
const struct config_enum_entry * entry ;
bool found ;
/* Reject duplicates for generic log level. */
if ( genericlevel ! = - 1 )
{
GUC_check_errdetail ( " Redundant specification of default log level. " ) ;
goto lmm_fail ;
}
/* Validate the log level */
found = false ;
for ( entry = server_message_level_options ; entry & & entry - > name ; entry + + )
{
if ( pg_strcasecmp ( entry - > name , elem ) = = 0 )
{
genericlevel = entry - > val ;
found = true ;
break ;
}
}
if ( ! found )
{
GUC_check_errdetail ( " Unrecognized log level: \" %s \" . " , elem ) ;
goto lmm_fail ;
}
}
else
{
char * loglevel = sep + 1 ;
char * ptype = elem ;
bool found ;
int level ;
const struct config_enum_entry * entry ;
/*
* Temporarily clobber the ' : ' with a string terminator , so that
* we can validate it . We restore this at the bottom .
*/
* sep = ' \0 ' ;
/* Validate the log level */
found = false ;
for ( entry = server_message_level_options ; entry & & entry - > name ; entry + + )
{
if ( pg_strcasecmp ( entry - > name , loglevel ) = = 0 )
{
level = entry - > val ;
found = true ;
break ;
}
}
if ( ! found )
{
GUC_check_errdetail ( " Unrecognized log level for process type \" %s \" : \" %s \" . " ,
ptype , loglevel ) ;
goto lmm_fail ;
}
/* Is the process type name valid and unique? */
found = false ;
for ( int i = 0 ; i < BACKEND_NUM_TYPES ; i + + )
{
if ( pg_strcasecmp ( process_types [ i ] , ptype ) = = 0 )
{
/* Reject duplicates for a process type. */
if ( assigned [ i ] )
{
GUC_check_errdetail ( " Redundant log level specification for process type \" %s \" . " ,
ptype ) ;
goto lmm_fail ;
}
newlevel [ i ] = level ;
assigned [ i ] = true ;
found = true ;
/*
* note : we must keep looking ! some process types appear
* multiple times in proctypelist . h .
*/
}
}
if ( ! found )
{
GUC_check_errdetail ( " Unrecognized process type \" %s \" . " , ptype ) ;
goto lmm_fail ;
}
/* Put the separator back in place */
* sep = ' : ' ;
}
/* all good */
continue ;
lmm_fail :
guc_free ( rawstring ) ;
list_free ( elemlist ) ;
return false ;
}
/*
* The generic log level must be specified . It is the fallback value .
*/
if ( genericlevel = = - 1 )
{
GUC_check_errdetail ( " Default log level was not defined. " ) ;
guc_free ( rawstring ) ;
list_free ( elemlist ) ;
return false ;
}
/* Apply the default log level to all processes not listed. */
for ( int i = 0 ; i < BACKEND_NUM_TYPES ; i + + )
{
if ( ! assigned [ i ] )
newlevel [ i ] = genericlevel ;
}
/*
* Save an ordered representation of the user - specified string , for the
* show_hook .
*/
list_sort ( elemlist , log_min_messages_cmp ) ;
initStringInfoExt ( & buf , strlen ( rawstring ) + 1 ) ;
foreach_ptr ( char , elem , elemlist )
{
if ( foreach_current_index ( elem ) = = 0 )
appendStringInfoString ( & buf , elem ) ;
else
appendStringInfo ( & buf , " , %s " , elem ) ;
}
result = ( char * ) guc_malloc ( LOG , buf . len + 1 ) ;
if ( ! result )
return false ;
memcpy ( result , buf . data , buf . len ) ;
result [ buf . len ] = ' \0 ' ;
guc_free ( * newval ) ;
* newval = result ;
guc_free ( rawstring ) ;
list_free ( elemlist ) ;
pfree ( buf . data ) ;
/*
* Pass back data for assign_log_min_messages to use .
*/
* extra = guc_malloc ( LOG , BACKEND_NUM_TYPES * sizeof ( int ) ) ;
if ( ! * extra )
return false ;
memcpy ( * extra , newlevel , BACKEND_NUM_TYPES * sizeof ( int ) ) ;
return true ;
}
/*
* list_sort ( ) callback for check_log_min_messages . The default element
* goes first ; the rest are ordered by strcmp ( ) of the process type .
*/
static int
log_min_messages_cmp ( const ListCell * a , const ListCell * b )
{
const char * s = lfirst ( a ) ;
const char * t = lfirst ( b ) ;
if ( strchr ( s , ' : ' ) = = NULL )
return - 1 ;
else if ( strchr ( t , ' : ' ) = = NULL )
return 1 ;
else
return strcmp ( s , t ) ;
}
/*
* GUC assign_hook for log_min_messages
*/
void
assign_log_min_messages ( const char * newval , void * extra )
{
for ( int i = 0 ; i < BACKEND_NUM_TYPES ; i + + )
log_min_messages [ i ] = ( ( int * ) extra ) [ i ] ;
}
/*
* GUC check_hook for backtrace_functions
*