?'; $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"); 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); } 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); } 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 *'); $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'])); #Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']); #$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() : ''; $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; } $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'; $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 ) { ZM\Logger::Debug("MinTime: $minTime"); if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) { # This handles sub second precision $minTime = strtotime($matches[1]).$matches[2]; ZM\Logger::Debug("MinTime: $minTime"); } 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 ( ! file_exists(ZM_DIR_EXPORTS) ) { ZM\Logger::Debug('Creating ' . ZM_DIR_EXPORTS); if ( ! mkdir(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 |