?'; $values[] = $minTime; } elseif ( $maxTime ) { $where[] = 'TimeKey < ?'; $values[] = $maxTime; } foreach ( $filter as $field=>$value ) { if ( !in_array($field, $filterFields) ) { ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true)); continue; } if ( $field == 'Level' ) { $where[] = $field.' <= ?'; $values[] = $value; } else { $where[] = $field.' = ?'; $values[] = $value; } } if ( count($where) ) $sql.= ' WHERE '.join(' AND ', $where); $sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit; return array('sql'=>$sql, 'values'=>$values); } # function buildLogQuery($action) switch ( $_REQUEST['task'] ) { case 'create' : { // Silently ignore bogus requests if ( !empty($_POST['level']) && !empty($_POST['message']) ) { ZM\logInit(array('id'=>'web_js')); $string = $_POST['message']; $file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : ''; if ( !empty($_POST['line']) ) { $line = validInt($_POST['line']); } else { $line = NULL; } $levels = array_flip(ZM\Logger::$codes); if ( !isset($levels[$_POST['level']]) ) { ZM\Panic('Unexpected logger level '.$_POST['level']); } $level = $levels[$_POST['level']]; ZM\Logger::fetch()->logPrint($level, $string, $file, $line); } else { ZM\Error('Invalid log create: '.print_r($_POST, true)); } ajaxResponse(); break; } case 'delete' : { if ( !canEdit('System') ) ajaxError('Insufficient permissions to delete log entries'); $query = buildLogQuery('DELETE'); $result = dbQuery($query['sql'], $query['values']); ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount())); } case 'query' : { if ( !canView('System') ) ajaxError('Insufficient permissions to view log entries'); $total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total'); $query = buildLogQuery('SELECT *'); global $Servers; if ( !$Servers ) $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $Servers as $server ) { $servers_by_Id[$server->Id()] = $server; } $logs = array(); $options = array(); foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) { $log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']); foreach ( $filterFields as $field ) { if ( !isset($options[$field]) ) $options[$field] = array(); $value = $log[$field]; if ( $field == 'Level' ) { if ( $value <= ZM\Logger::INFO ) $options[$field][$value] = ZM\Logger::$codes[$value]; else $options[$field][$value] = 'DB'.$value; } else if ( $field == 'ServerId' ) { $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : ''; } else if ( isset($log[$field]) ) { $options[$field][$log[$field]] = $value; } } $logs[] = $log; } # end foreach log db row foreach ( $options as $field => $values ) { asort($options[$field]); } $available = count($logs); ajaxResponse(array( 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG), 'total' => $total, 'available' => isset($available) ? $available : $total, 'logs' => $logs, 'state' => logState(), 'options' => $options, )); break; } case 'export' : { if ( !canView('System') ) ajaxError('Insufficient permissions to export logs'); $minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL; $maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL; if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) { $tempTime = $minTime; $minTime = $maxTime; $maxTime = $tempTime; } //$limit = isset($_POST['limit'])?$_POST['limit']:1000; $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = 'TimeKey'; if ( isset($_POST['sortField']) ) { if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) { ZM\Error('Invalid sort field '.$_POST['sortField']); } else { $sortField = $_POST['sortField']; } } $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc'; global $Servers; if ( !$Servers ) $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { $servers_by_Id[$server->Id()] = $server; } $sql = 'SELECT * FROM Logs'; $where = array(); $values = array(); if ( $minTime ) { if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) { # This handles sub second precision $minTime = strtotime($matches[1]).$matches[2]; } else { $minTime = strtotime($minTime); } $where[] = 'TimeKey >= ?'; $values[] = $minTime; } if ( $maxTime ) { if ( preg_match('/(.+)(\.\d+)/', $maxTime, $matches) ) { $maxTime = strtotime($matches[1]).$matches[2]; } else { $maxTime = strtotime($maxTime); } $where[] = 'TimeKey <= ?'; $values[] = $maxTime; } foreach ( $filter as $field=>$value ) { if ( $value != '' ) { if ( $field == 'Level' ) { $where[] = $field.' <= ?'; $values[] = $value; } else { $where[] = $field.' = ?'; $values[] = $value; } } } if ( count($where) ) $sql.= ' WHERE '.join(' AND ', $where); $sql .= ' ORDER BY '.$sortField.' '.$sortOrder; //$sql .= " limit ".dbEscape($limit); $format = isset($_POST['format']) ? $_POST['format'] : 'text'; switch ( $format ) { case 'text' : $exportExt = 'txt'; break; case 'tsv' : $exportExt = 'tsv'; break; case 'html' : $exportExt = 'html'; break; case 'xml' : $exportExt = 'xml'; break; default : ZM\Fatal("Unrecognised log export format '$format'"); } $exportKey = substr(md5(rand()), 0, 8); $exportFile = 'zm-log.'.$exportExt; if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) { ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } $exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt; ZM\Logger::Debug("Exporting to $exportPath"); if ( !($exportFP = fopen($exportPath, 'w')) ) ZM\Fatal("Unable to open log export file $exportPath"); $logs = array(); foreach ( dbFetchAll($sql, NULL, $values) as $log ) { $log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } ZM\Logger::Debug(count($logs).' lines being exported by '.$sql.implode(',', $values)); switch( $format ) { case 'text' : { foreach ( $logs as $log ) { if ( $log['Line'] ) fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']); else fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']); } break; } case 'tsv' : { # This line doesn't need fprintf, it could use fwrite fprintf($exportFP, join("\t", translate('DateTime'), translate('Component'), translate('Server'), translate('Pid'), translate('Level'), translate('Message'), translate('File'), translate('Line') )."\n"); foreach ( $logs as $log ) { fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']); } break; } case 'html' : { fwrite($exportFP, '
'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'
'.count($logs).' '.translate('Logs').'
'.translate('DateTime').' | '.translate('Component').' | '.translate('Server').' | '.translate('Pid').' | '.translate('Level').' | '.translate('Message').' | '.translate('File').' | '.translate('Line').' |
---|---|---|---|---|---|---|---|
%s | %s | %s | %d | %s | %s | %s | %s |