#region License
// Copyright (c) 2010, Jasper Yeh.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of ClearCanvas Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
#endregion
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using ClearCanvas.Common;
using ClearCanvas.Desktop;
using ClearCanvas.Desktop.Actions;
using ClearCanvas.Desktop.Tools;
using Nullstack.ClearCanvasEx.DevTools.Common;
using Path = System.IO.Path;
namespace Nullstack.ClearCanvasEx.DevTools.Logging
{
///
/// Tools for opening logs related to the viewer and the service.
///
[GroupHint("openWksLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Workstation.Open")]
[MenuAction("openWksLog", "global-menus/mnDebug/mnWorkstationLog/mnOpen", "OpenWorkstationLog")]
//
[GroupHint("tailWksLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Workstation.Tail")]
[MenuAction("tailWksLog", "global-menus/mnDebug/mnWorkstationLog/mnTail", "TailWorkstationLog")]
//
[GroupHint("deskWksLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Workstation.Desk")]
[MenuAction("deskWksLog", "global-menus/mnDebug/mnWorkstationLog/mnMonitor", "DeskWorkstationLog")]
//
[GroupHint("openSrvLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Service.Open")]
[MenuAction("openSrvLog", "global-menus/mnDebug/mnServiceLog/mnOpen", "OpenServiceLog")]
//
[GroupHint("tailSrvLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Service.Tail")]
[MenuAction("tailSrvLog", "global-menus/mnDebug/mnServiceLog/mnTail", "TailServiceLog")]
//
[GroupHint("deskSrvLog", "Nullstack.ClearCanvasEx.DevTools.Logs.Service.Desk")]
[MenuAction("deskSrvLog", "global-menus/mnDebug/mnServiceLog/mnMonitor", "DeskServiceLog")]
//
[ExtensionOf(typeof (DesktopToolExtensionPoint))]
public class AppLogViewerTool : Tool
{
private const string _workstationLogFilename = "logs\\ClearCanvas.Workstation.log";
private const string _serviceLogFilename = "logs\\ClearCanvas.Server.ShredHostService.log";
private static readonly Regex _commandLinePattern = new Regex("((?:\"[^\"]+\")|\\S+)(?: (.*))?", RegexOptions.Compiled | RegexOptions.Singleline);
public static string WorkstationLogFile
{
get
{
var process = CCProcess.GetDesktopProcess();
return process != null ? FindLogFile(process.WorkingDirectory, _workstationLogFilename, _serviceLogFilename) : string.Empty;
}
}
public static string ServiceLogFile
{
get
{
var process = CCProcess.GetServiceProcess();
return process != null ? FindLogFile(process.WorkingDirectory, _serviceLogFilename, _workstationLogFilename) : string.Empty;
}
}
private static string FindLogFile(string basePath, params string[] possibleFilenames)
{
foreach (var possibleFilename in possibleFilenames)
{
var path = Path.Combine(basePath, possibleFilename);
if (File.Exists(path))
return path;
}
return string.Empty;
}
private Shelf _shelfWorkstation;
private Shelf _shelfService;
///
/// Launches the workstation log in an external viewer
///
public void OpenWorkstationLog()
{
LaunchFile(WorkstationLogFile);
}
///
/// Launches the last few kb of the workstation log in an external viewer
///
public void TailWorkstationLog()
{
LaunchRecentFile(WorkstationLogFile);
}
///
/// Launches the workstation log in the viewer component
///
public void DeskWorkstationLog()
{
// note: we use this field to ensure we don't open multiple copies of the same log viewer
if (_shelfWorkstation == null)
{
var file = WorkstationLogFile;
if (string.IsNullOrEmpty(file) || !File.Exists(file))
return;
var component = new LogViewerComponent(file);
_shelfWorkstation = ApplicationComponent.LaunchAsShelf(Context.DesktopWindow, component, SR.szWorkstationLog, "DesktopWorkstationLog", ShelfDisplayHint.DockFloat);
_shelfWorkstation.Closed += OnWorkstationShelfClosed;
}
}
///
/// callback for when the viewer closes
///
///
///
private void OnWorkstationShelfClosed(object sender, ClosedEventArgs e)
{
_shelfWorkstation = null;
}
///
/// Launches the service log in the viewer component
///
public void DeskServiceLog()
{
// note: we use this field to ensure we don't open multiple copies of the same log viewer
if (_shelfService == null)
{
var file = ServiceLogFile;
if (string.IsNullOrEmpty(file) || !File.Exists(file))
return;
var component = new LogViewerComponent(file);
_shelfService = ApplicationComponent.LaunchAsShelf(Context.DesktopWindow, component, SR.szServiceLog, "DesktopServiceLog", ShelfDisplayHint.DockFloat);
_shelfService.Closed += OnServiceShelfClosed;
}
}
///
/// callback for when the viewer closes
///
///
///
private void OnServiceShelfClosed(object sender, ClosedEventArgs e)
{
_shelfService = null;
}
///
/// Opens the service log in an external viewer.
///
public void OpenServiceLog()
{
LaunchFile(ServiceLogFile);
}
///
/// Opens the last few KB of the service log in an external viewer.
///
public void TailServiceLog()
{
LaunchRecentFile(ServiceLogFile);
}
///
/// Helper method to copy the last 4 KB of the specified file into a temp file and launch it using the external viewer
///
/// The filename
private static void LaunchRecentFile(string basefile)
{
const int tailSize = 4096;
const int bufferSize = 100;
if (!File.Exists(basefile))
return;
try
{
string tempfile = Path.GetTempFileName();
FileStream outfile = File.Open(tempfile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
FileStream fs = File.Open(basefile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
byte[] buffer = new byte[bufferSize];
if (fs.Length > tailSize)
{
fs.Seek(-tailSize, SeekOrigin.End);
}
int count;
do
{
count = fs.Read(buffer, 0, bufferSize);
if (count > 0)
{
outfile.Write(buffer, 0, count);
}
} while (count > 0);
outfile.Close();
fs.Close();
string xfile = tempfile + ".txt";
File.Move(tempfile, xfile);
int pid = LaunchFile(xfile);
ProcessWaiter pw = new ProcessWaiter(pid, xfile);
Thread t = new Thread(new ThreadStart(pw.Execute));
t.IsBackground = true;
t.Start();
}
catch (IOException) {}
}
///
/// Launches the file using the configured external viewer
///
/// The filename
/// Returns the process id of the external viewer
private static int LaunchFile(string filename)
{
string command = filename;
string arguments = "";
if (!File.Exists(filename))
return 0;
if (!ConfigurationHelper.LogViewAppPathIsEmpty)
{
FileInfo fi = new FileInfo(filename);
Match m = _commandLinePattern.Match(ConfigurationHelper.LogViewAppPath);
if (m.Success)
{
command = m.Groups[1].Value;
command = command.Replace("%n", fi.Name);
command = command.Replace("%p", fi.FullName);
command = command.Replace("%d", fi.DirectoryName);
command = command.Replace("%x", fi.Extension);
command = command.Replace("%%", "%");
arguments = m.Groups[2].Value;
arguments = arguments.Replace("%n", fi.Name);
arguments = arguments.Replace("%p", fi.FullName);
arguments = arguments.Replace("%d", fi.DirectoryName);
arguments = arguments.Replace("%x", fi.Extension);
arguments = arguments.Replace("%%", "%");
}
else
{
command = ConfigurationHelper.LogViewAppPath;
}
}
ProcessStartInfo nfo = new ProcessStartInfo(command, arguments);
nfo.WorkingDirectory = Environment.CurrentDirectory;
Process p = Process.Start(nfo);
return p.Id;
}
///
/// A class to delete a file after a certain process ends
///
private class ProcessWaiter
{
private readonly int _pid;
private readonly string _filename;
public ProcessWaiter(int pid, string filename)
{
_pid = pid;
_filename = filename;
}
public void Execute()
{
Process p = Process.GetProcessById(_pid);
p.WaitForExit();
File.Delete(_filename);
}
}
}
}