diff -u -r b/doc/src/sgml/config.sgml a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml 2013-09-09 23:40:52.356371191 +1200 +++ a/doc/src/sgml/config.sgml 2013-09-19 22:13:26.236681468 +1200 @@ -3942,7 +3942,13 @@ Unrecognized escapes are ignored. Other characters are copied straight to the log line. Some escapes are only recognized by session processes, and are ignored by - background processes such as the main server process. + background processes such as the main server process. Status + information may be aligned either left or right by specifying a + numeric literal after the % and before the option. A negative + padding will cause the status information to be padded on the + right with spaces to give it a minimum width, whereas a positive + padding will pad on the left. Padding can be useful to aid human + readability in log files. This parameter can only be set in the postgresql.conf file or on the server command line. The default is an empty string. diff -u -r b/src/backend/utils/error/elog.c a/src/backend/utils/error/elog.c --- b/src/backend/utils/error/elog.c 2013-09-09 23:40:55.160371254 +1200 +++ a/src/backend/utils/error/elog.c 2013-09-19 21:55:41.024657448 +1200 @@ -167,6 +167,7 @@ } while (0) +static const char *process_log_prefix_padding(const char *p, int *padding); 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); @@ -2120,6 +2121,43 @@ } /* + * helper function for processing the format string in + * log_line_prefix() + * This function returns NULL if it finds something which + * it deems invalid for the log_line_prefix string. + */ +static const char +*process_log_prefix_padding(const char *p, int *ppadding) +{ + int paddingsign = 1; + int padding = 0; + + if (*p == '-') + { + p++; + + if (*p == '\0') /* Did the buf end in %- ? */ + return NULL; + paddingsign = -1; + } + + + /* generate an int version of the numerical string */ + while (*p >= '0' && *p <= '9') + padding = padding * 10 + (*p++ - '0'); + + /* format is invalid if it ends with the padding number */ + if (*p == '\0') + return NULL; + + padding *= paddingsign; + *ppadding = padding; + return p; + +} + + +/* * Format tag info for log lines; append to the provided buffer. */ static void @@ -2129,10 +2167,9 @@ static long log_line_number = 0; /* has counter been reset in current process? */ - static int log_my_pid = 0; - - int format_len; - int i; + static int log_my_pid = 0; + int padding; + const char *p; /* * This is one of the few places where we'd rather not inherit a static @@ -2151,23 +2188,43 @@ if (Log_line_prefix == NULL) return; /* in case guc hasn't run yet */ - format_len = strlen(Log_line_prefix); - - for (i = 0; i < format_len; i++) + /* + * The Log_line_prefix also supports optional space padding similar + * to printf(). e.g. %-10u will pad the right side of the username + * to make it at least 10 chars wide. %10u would pad the left side. + */ + for (p = Log_line_prefix; *p != '\0'; p++) { - if (Log_line_prefix[i] != '%') + if (*p != '%') { /* literal char, just copy */ - appendStringInfoChar(buf, Log_line_prefix[i]); + appendStringInfoChar(buf, *p); continue; } - /* go to char after '%' */ - i++; - if (i >= format_len) + + /* must be a '%', so skip to the next char */ + p++; + if (*p == '\0') break; /* format error - ignore it */ + else if (*p == '%') + { + /* string contains %% */ + appendStringInfoChar(buf, '%'); + continue; + } + + + /* + * Process any formatting which may exist after the '%'. + * Note that this moves p past the padding number if it + * exists. + */ + if ((p = process_log_prefix_padding(p, &padding)) == NULL) + break; + /* process the option */ - switch (Log_line_prefix[i]) + switch (*p) { case 'a': if (MyProcPort) @@ -2176,8 +2233,13 @@ if (appname == NULL || *appname == '\0') appname = _("[unknown]"); - appendStringInfoString(buf, appname); + appendStringInfo(buf, "%*s", padding, appname); + } + else + { + appendStringInfoSpaces(buf, padding); } + break; case 'u': if (MyProcPort) @@ -2186,7 +2248,11 @@ if (username == NULL || *username == '\0') username = _("[unknown]"); - appendStringInfoString(buf, username); + appendStringInfo(buf, "%*s", padding, username); + } + else + { + appendStringInfoSpaces(buf, padding); } break; case 'd': @@ -2196,21 +2262,30 @@ if (dbname == NULL || *dbname == '\0') dbname = _("[unknown]"); - appendStringInfoString(buf, dbname); + appendStringInfo(buf, "%*s", padding, dbname); + } + else + { + appendStringInfoSpaces(buf, padding); } break; case 'c': - appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid); + { + char strfbuf[128]; + snprintf(strfbuf, sizeof(strfbuf) - 1, "%lx.%x", + (long) (MyStartTime), MyProcPid); + appendStringInfo(buf, "%*s", padding, strfbuf); + } break; case 'p': - appendStringInfo(buf, "%d", MyProcPid); + appendStringInfo(buf, "%*d", padding, MyProcPid); break; case 'l': - appendStringInfo(buf, "%ld", log_line_number); + appendStringInfo(buf, "%*ld", padding, log_line_number); break; case 'm': setup_formatted_log_time(); - appendStringInfoString(buf, formatted_log_time); + appendStringInfo(buf, "%*s", padding, formatted_log_time); break; case 't': { @@ -2220,13 +2295,13 @@ pg_strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", pg_localtime(&stamp_time, log_timezone)); - appendStringInfoString(buf, strfbuf); + appendStringInfo(buf, "%*s", padding, strfbuf); } break; case 's': if (formatted_start_time[0] == '\0') setup_formatted_start_time(); - appendStringInfoString(buf, formatted_start_time); + appendStringInfo(buf, "%*s", padding, formatted_start_time); break; case 'i': if (MyProcPort) @@ -2235,43 +2310,70 @@ int displen; psdisp = get_ps_display(&displen); - appendBinaryStringInfo(buf, psdisp, displen); + appendStringInfo(buf, "%*s", padding, psdisp); + } + else + { + appendStringInfoSpaces(buf, padding); } break; case 'r': if (MyProcPort && MyProcPort->remote_host) { - appendStringInfoString(buf, MyProcPort->remote_host); - if (MyProcPort->remote_port && - MyProcPort->remote_port[0] != '\0') - appendStringInfo(buf, "(%s)", - MyProcPort->remote_port); + if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') + { + /* + * This option is slightly special as the port number + * may be appended onto the end. Here we need to build + * 1 string which contains the remote_host and optionally + * the remote_port (if set) so we can properly align the + * string. + */ + + char *hostport; + int alloclen = strlen(MyProcPort->remote_host) + + strlen(MyProcPort->remote_port) + 3; + hostport = palloc(alloclen); + sprintf(hostport, "%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port); + appendStringInfo(buf, "%*s", padding, hostport); + pfree(hostport); + + } + else + { + appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host); + } + } + else + { + appendStringInfoSpaces(buf, padding); } break; case 'h': if (MyProcPort && MyProcPort->remote_host) - appendStringInfoString(buf, MyProcPort->remote_host); + appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host); + else + appendStringInfoSpaces(buf, padding); break; case 'q': /* in postmaster and friends, stop if %q is seen */ /* in a backend, just ignore */ if (MyProcPort == NULL) - i = format_len; + return; 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); + else + appendStringInfoSpaces(buf, padding); break; case 'x': - appendStringInfo(buf, "%u", GetTopTransactionIdIfAny()); + appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny()); break; case 'e': - appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode)); - break; - case '%': - appendStringInfoChar(buf, '%'); + appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode)); break; default: /* format error - ignore it */