Merge pull request #1457 from APriestman/caseImporter

Improved SingleUserCaseImporter
This commit is contained in:
Richard Cordovano 2015-07-23 10:13:55 -04:00
commit 67c853e7ee
8 changed files with 122 additions and 183 deletions

View File

@ -255,4 +255,5 @@ CaseConverter.NonUniqueDatabaseName=Database name not unique. Skipping.
CaseConverter.PotentiallyNonUniqueDatabaseName=Unclear if database name unique. Moving ahead.
CaseConverter.ConvertedToMultiUser=This case was converted to a Multi-user collaborative case on
CaseConverter.UnableToCopySourceImages=Unable to copy source images
CaseConverter.ConversionSuccessful=. Conversion successful:
CaseConverter.ConversionSuccessful=. Conversion successful:
CaseConverter.DeletingCase=Deleting original case folder

View File

@ -61,6 +61,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
@ -1049,7 +1050,7 @@ public class Case {
public String getModuleOutputDirectoryRelativePath() {
Path thePath;
if (getCaseType() == CaseType.MULTI_USER_CASE) {
thePath = Paths.get(getLocalHostName(), MODULE_FOLDER);
thePath = Paths.get(NetworkUtils.getLocalHostName(), MODULE_FOLDER);
} else {
thePath = Paths.get(MODULE_FOLDER);
}
@ -1062,7 +1063,7 @@ public class Case {
* Get the host output directory path where modules should save their
* permanent data. If single-user case, the directory is a subdirectory of
* the case directory. If multi-user case, the directory is a subdirectory
* of HostName, which is a subdirectory of the case directory.
* of the hostName, which is a subdirectory of the case directory.
*
* @return the path to the host output directory
*/
@ -1070,7 +1071,7 @@ public class Case {
String caseDirectory = getCaseDirectory();
Path hostPath;
if (caseType == CaseType.MULTI_USER_CASE) {
hostPath = Paths.get(caseDirectory, getLocalHostName());
hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
} else {
hostPath = Paths.get(caseDirectory);
}
@ -1386,7 +1387,7 @@ public class Case {
String hostClause = "";
if (caseType == CaseType.MULTI_USER_CASE) {
hostClause = File.separator + getLocalHostName();
hostClause = File.separator + NetworkUtils.getLocalHostName();
}
result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs()
&& (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
@ -1618,24 +1619,5 @@ public class Case {
return hasData;
}
/**
* Set the host name variable. Sometimes the network can be finicky, so the
* answer returned by getHostName() could throw an exception or be null.
* Have it read the environment variable if getHostName() is unsuccessful.
*/
public static String getLocalHostName() {
if (HostName == null || HostName.isEmpty()) {
try {
HostName = java.net.InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException ex) {
// getLocalHost().getHostName() can fail in some situations.
// Use environment variable if so.
HostName = System.getenv("COMPUTERNAME");
}
if (HostName == null || HostName.isEmpty()) {
HostName = System.getenv("COMPUTERNAME");
}
}
return HostName;
}
}

View File

@ -22,7 +22,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
@ -42,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent;
import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
@ -84,7 +84,7 @@ final class CollaborationMonitor {
* Get the local host name so it can be used to identify the source of
* collaboration tasks broadcast by this node.
*/
hostName = getHostName();
hostName = NetworkUtils.getLocalHostName();
/**
* Create an event publisher that will be used to communicate with
@ -124,24 +124,6 @@ final class CollaborationMonitor {
periodicTasksExecutor.scheduleAtFixedRate(new StaleTaskDetectionTask(), STALE_TASKS_DETECTION_INTERVAL_MINUTES, STALE_TASKS_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
}
/**
* Determines the name of the local host for use in describing local tasks.
*
* @return The host name of this Autopsy node.
*/
private static String getHostName() {
String name;
try {
name = java.net.InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException notUsed) {
name = System.getenv("COMPUTERNAME");
}
if (name.isEmpty()) {
name = "Collaborator";
}
return name;
}
/**
* Shuts down this collaboration monitor.
*/

View File

@ -39,30 +39,25 @@ import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import org.apache.commons.io.FileUtils;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
/**
* Convert case(s) from single-user to multi-user. Recursively scans subfolders.
*/
public class CaseConverter implements Runnable {
public class SingleUserCaseImporter implements Runnable {
private static final String AUTOPSY_DB_FILE = "autopsy.db"; //NON-NLS
private static final String DOTAUT = ".aut"; //NON-NLS
private static final String CACHE_FOLDER = "Cache"; //NON-NLS
private static final String EXPORT_FOLDER = "Export"; //NON-NLS
private static final String LOG_FOLDER = "Log"; //NON-NLS
private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
private static final String TEMP_FOLDER = "Temp"; //NON-NLS
private static final String TIMELINE_FOLDER = "Timeline"; //NON-NLS
private final static String AIM_LOG_FILE_NAME = "auto_ingest_log.txt"; //NON-NLS
private final static String TIMELINE_FILE = "events.db"; //NON-NLS
public static final String CASE_CONVERSION_LOG_FILE = "case_conversion.txt"; //NON-NLS
public static final String CASE_CONVERSION_LOG_FILE = "case_import_log.txt"; //NON-NLS
private static final String logDateFormat = "yyyy/MM/dd HH:mm:ss"; //NON-NLS
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(logDateFormat);
private static final int MAX_DB_NAME_LENGTH = 63;
@ -71,6 +66,8 @@ public class CaseConverter implements Runnable {
private final String caseOutputFolder;
private final String imageInputFolder;
private final String imageOutputFolder;
private final boolean copyImages;
private final boolean deleteCase;
private final CaseDbConnectionInfo db;
private final ConversionDoneCallback notifyOnComplete;
private PrintWriter writer;
@ -86,14 +83,19 @@ public class CaseConverter implements Runnable {
* @param imageInput the folder that holds the images to copy over
* @param imageOutput the destination folder for the images
* @param database the connection information to talk to the PostgreSQL db
* @param copyImages true if images should be copied
* @param deleteCase true if the old version of the case should be deleted after import
* @param callback a callback from the calling panel for notification when
* the conversion has completed. This is a Runnable on a different thread.
*/
public CaseConverter(String caseInput, String caseOutput, String imageInput, String imageOutput, CaseDbConnectionInfo database, ConversionDoneCallback callback) {
public SingleUserCaseImporter(String caseInput, String caseOutput, String imageInput, String imageOutput, CaseDbConnectionInfo database,
boolean copyImages, boolean deleteCase, ConversionDoneCallback callback) {
this.caseInputFolder = caseInput;
this.caseOutputFolder = caseOutput;
this.imageInputFolder = imageInput;
this.imageOutputFolder = imageOutput;
this.copyImages = copyImages;
this.deleteCase = deleteCase;
this.db = database;
this.notifyOnComplete = callback;
}
@ -115,7 +117,12 @@ public class CaseConverter implements Runnable {
try {
log("Beginning to convert " + input.toString() + " to " + caseOutputFolder + "\\" + oldCaseFolder); //NON-NLS
checkInput(input.toFile(), new File(imageInputFolder));
if(copyImages){
checkInput(input.toFile(), new File(imageInputFolder));
}
else{
checkInput(input.toFile(), null);
}
String oldCaseName = oldCaseFolder;
if (TimeStampUtils.endsWithTimeStamp(oldCaseName)) {
oldCaseName = oldCaseName.substring(0, oldCaseName.length() - TimeStampUtils.getTimeStampLength());
@ -127,7 +134,7 @@ public class CaseConverter implements Runnable {
// read old xml config
oldXmlCaseManagement.open(input.resolve(oldCaseName + DOTAUT).toString());
if (oldXmlCaseManagement.getCaseType() == CaseType.MULTI_USER_CASE) {
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.AlreadyMultiUser"));
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.AlreadyMultiUser"));
}
String newCaseFolder = prepareOutput(caseOutputFolder, oldCaseFolder);
@ -144,10 +151,12 @@ public class CaseConverter implements Runnable {
File imageDestination = Paths.get(imageOutputFolder, caseName).toFile();
copyResults(input, newCaseFolder); // Copy items to new hostname folder structure
copyResults(input, newCaseFolder, oldCaseName); // Copy items to new hostname folder structure
dbName = convertDb(dbName, input, newCaseFolder); // Change from SQLite to PostgreSQL
File imageSource = copyInputImages(imageInputFolder, oldCaseName, imageDestination); // Copy images over
fixPaths(imageSource.toString(), imageDestination.toString(), dbName); // Update paths in DB
if(copyImages){
File imageSource = copyInputImages(imageInputFolder, oldCaseName, imageDestination); // Copy images over
fixPaths(imageSource.toString(), imageDestination.toString(), dbName); // Update paths in DB
}
// create new XML config
newXmlCaseManagement.create(Paths.get(caseOutputFolder, newCaseFolder).toString(),
@ -159,8 +168,17 @@ public class CaseConverter implements Runnable {
// Set created date. This calls writefile, no need to call it again
newXmlCaseManagement.setCreatedDate(oldXmlCaseManagement.getCreatedDate());
log(NbBundle.getMessage(CaseConverter.class, "CaseConverter.FinishedConverting")
+ input.toString() + NbBundle.getMessage(CaseConverter.class, "CaseConverter.To")
// At this point the import has been finished successfully so we can delete the original case
// (if requested). This *should* be fairly safe - at this point we know there was an autopsy file
// and database in the given directory so the user shouldn't be able to accidently blow away
// their C drive.
if(deleteCase){
log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.DeletingCase") + " " + input);
FileUtils.deleteDirectory(input.toFile());
}
log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.FinishedConverting")
+ input.toString() + NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.To")
+ caseOutputFolder + File.separatorChar + newCaseFolder);
} catch (Exception exp) {
/// clean up here
@ -174,18 +192,18 @@ public class CaseConverter implements Runnable {
* Ensure the input source has an autopsy.db and exists.
*
* @param caseInput The folder containing a case to convert.
* @param imageInput The folder containing the images to copy.
* @param imageInput The folder containing the images to copy or null if images are not being copied.
* @throws Exception
*/
private void checkInput(File caseInput, File imageInput) throws Exception {
if (false == caseInput.exists()) {
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.BadCaseSourceFolder"));
} else if (false == imageInput.exists()) {
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.BadImageSourceFolder"));
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadCaseSourceFolder"));
} else if ((imageInput != null) && (false == imageInput.exists())) {
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadImageSourceFolder"));
}
Path path = Paths.get(caseInput.toString(), AUTOPSY_DB_FILE);
if (false == path.toFile().exists()) {
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.BadDatabaseFileName"));
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadDatabaseFileName"));
}
}
@ -213,7 +231,7 @@ public class CaseConverter implements Runnable {
while (specificOutputFolder.exists()) {
if (number == Integer.MAX_VALUE) {
// oops. it never became unique. give up.
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.NonUniqueOutputFolder") + caseFolder);
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.NonUniqueOutputFolder") + caseFolder);
}
temp = sanitizedCaseName + "_" + Integer.toString(number) + timeStamp; //NON-NLS
specificOutputFolder = Paths.get(caseOutputFolder, temp).toFile();
@ -235,62 +253,24 @@ public class CaseConverter implements Runnable {
* @param newCaseFolder deconflicted case name for the new multi-user case
* @throws IOException
*/
private void copyResults(Path input, String newCaseFolder) throws IOException {
private void copyResults(Path input, String newCaseFolder, String caseName) throws IOException {
/// get hostname
String hostName = Case.getLocalHostName();
Path source = input.resolve(CACHE_FOLDER);
Path destination;
String hostName = NetworkUtils.getLocalHostName();
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, CACHE_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
if(input.toFile().exists()){
Path destination = Paths.get(caseOutputFolder, newCaseFolder, hostName);
FileUtils.copyDirectory(input.toFile(), destination.toFile());
}
source = input.resolve(EXPORT_FOLDER);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, EXPORT_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
// Remove the single-user .aut file and database
File oldDatabaseFile = Paths.get(caseOutputFolder, newCaseFolder, hostName, caseName + ".aut").toFile();
if(oldDatabaseFile.exists()){
oldDatabaseFile.delete();
}
source = input.resolve(LOG_FOLDER);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, LOG_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
}
source = input.resolve(MODULE_FOLDER);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, MODULE_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
}
source = input.resolve(REPORTS_FOLDER);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, REPORTS_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
}
source = input.resolve(TEMP_FOLDER);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, TEMP_FOLDER);
FileUtils.copyDirectory(source.toFile(), destination.toFile());
}
source = input.resolve(TIMELINE_FILE);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, hostName, MODULE_FOLDER, TIMELINE_FOLDER, TIMELINE_FILE);
FileUtils.copyFile(source.toFile(), destination.toFile());
}
source = input.resolve(AIM_LOG_FILE_NAME);
if (source.toFile().exists()) {
destination = Paths.get(caseOutputFolder, newCaseFolder, AIM_LOG_FILE_NAME);
FileUtils.copyFile(source.toFile(), destination.toFile());
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(destination.toString(), true)))) {
out.println(NbBundle.getMessage(CaseConverter.class, "CaseConverter.ConvertedToMultiUser") + new Date());
} catch (IOException e) {
// if unable to log it, no problem
}
File oldAutopsyFile = Paths.get(caseOutputFolder, newCaseFolder, hostName, "autopsy.db").toFile();
if(oldAutopsyFile.exists()){
oldAutopsyFile.delete();
}
}
@ -904,6 +884,7 @@ public class CaseConverter implements Runnable {
numberingPK = postgresqlConnection.createStatement();
numberingPK.execute("ALTER SEQUENCE blackboard_artifact_tags_tag_id_seq RESTART WITH " + (biggestPK + 1)); //NON-NLS
sqliteConnection.close();
postgresqlConnection.close();
return dbName;
@ -944,7 +925,7 @@ public class CaseConverter implements Runnable {
// not unique. add numbers before dbName.
if (number == Integer.MAX_VALUE) {
// oops. it never became unique. give up.
throw new Exception(NbBundle.getMessage(CaseConverter.class, "CaseConverter.NonUniqueDatabaseName"));
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.NonUniqueDatabaseName"));
}
sanitizedDbName = "_" + Integer.toString(number) + "_" + baseDbName; //NON-NLS
@ -959,7 +940,7 @@ public class CaseConverter implements Runnable {
} else {
// Could be caused by database credentials, using user accounts that
// can not check if other databases exist, so allow it to continue
log(NbBundle.getMessage(CaseConverter.class, "CaseConverter.PotentiallyNonUniqueDatabaseName"));
log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.PotentiallyNonUniqueDatabaseName"));
}
return sanitizedDbName;
@ -992,7 +973,7 @@ public class CaseConverter implements Runnable {
if (chosenInput != null && chosenInput.exists()) {
FileUtils.copyDirectory(chosenInput, output);
} else {
log(NbBundle.getMessage(CaseConverter.class, "CaseConverter.UnableToCopySourceImages"));
log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.UnableToCopySourceImages"));
}
return chosenInput;
}
@ -1005,7 +986,7 @@ public class CaseConverter implements Runnable {
/// Fix paths in reports, tsk_files_path, and tsk_image_names tables
Connection postgresqlConnection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/" + dbName, db.getUserName(), db.getPassword()); //NON-NLS
String hostName = Case.getLocalHostName();
String hostName = NetworkUtils.getLocalHostName();
// add hostname to reports
Statement updateStatement = postgresqlConnection.createStatement();
@ -1157,7 +1138,7 @@ public class CaseConverter implements Runnable {
writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true);
} catch (IOException ex) {
writer = null;
Exceptions.printStackTrace(ex);
Logger.getLogger(SingleUserCaseImporter.class.getName()).log(Level.WARNING, "Error opening log file " + logFile.toString(), ex);
}
}
@ -1180,11 +1161,11 @@ public class CaseConverter implements Runnable {
* not. True if all was successful, false otherwise.
*/
private void closeLog(boolean result) {
log(NbBundle.getMessage(CaseConverter.class, "CaseConverter.FinishedConverting")
log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.FinishedConverting")
+ caseInputFolder
+ NbBundle.getMessage(CaseConverter.class, "CaseConverter.To")
+ NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.To")
+ caseOutputFolder
+ NbBundle.getMessage(CaseConverter.class, "CaseConverter.ConversionSuccessful")
+ NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.ConversionSuccessful")
+ result);
if (writer != null) {

View File

@ -45,7 +45,7 @@ import org.xml.sax.SAXException;
*
* @author jantonius
*/
public class XMLCaseManagement implements CaseConfigFileInterface {
class XMLCaseManagement implements CaseConfigFileInterface {
final static String XSDFILE = "CaseSchema.xsd"; //NON-NLS
final static String TOP_ROOT_NAME = "AutopsyCase"; //NON-NLS

View File

@ -0,0 +1,43 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import java.net.UnknownHostException;
public class NetworkUtils {
/**
* Set the host name variable. Sometimes the network can be finicky, so the
* answer returned by getHostName() could throw an exception or be null.
* Have it read the environment variable if getHostName() is unsuccessful.
*/
public static String getLocalHostName() {
String hostName = "";
try {
hostName = java.net.InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException ex) {
// getLocalHost().getHostName() can fail in some situations.
// Use environment variable if so.
hostName = System.getenv("COMPUTERNAME");
}
if (hostName == null || hostName.isEmpty()) {
hostName = System.getenv("COMPUTERNAME");
}
return hostName;
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.coreutils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -26,8 +26,8 @@ import java.util.regex.Pattern;
*/
public final class TimeStampUtils {
// Pattern to identify whether case name contains a time stamp generated by Auto Ingest.
// Sample case name created by auto-ingest: Case 1_2015_02_02_12_10_31 for case "Case 1"
// Pattern to identify whether case name contains a generated time stamp.
// Sample case name with time stamp: Case 1_2015_02_02_12_10_31 for case "Case 1"
private static final Pattern timeStampPattern = Pattern.compile("\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}$");
private static final int LENGTH_OF_DATE_TIME_STAMP = 20; // length of the above time stamp

View File

@ -1,50 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
/*
* Formatter to wrap another formatter and prepend a timestampe to each formatted string
* Not currently used.
*/
class TimestampingFormatter extends Formatter {
Formatter original;
DateFormat timestampFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US);
String lineSeparator = System.getProperty("line.separator");
TimestampingFormatter(Formatter original) {
this.original = original;
}
@Override
public String format(LogRecord record) {
long millis = record.getMillis();
String timestamp = timestampFormat.format(new Date(millis));
return timestamp + lineSeparator + original.format(record);
}
}