diff --git a/docs-mini/README.logging b/docs-mini/README.logging
new file mode 100644
index 000000000..f3be54d0c
--- /dev/null
+++ b/docs-mini/README.logging
@@ -0,0 +1,75 @@
+Logging in FlightGear
+---------------------
+
+[Note: JSBSim also has its own independent logging facilities, which
+are not discussed here.]
+
+FlightGear can log any property values at any interval to one or more
+CSV files (which can be read and graphed using spreadsheets like
+Gnumeric or Excel). Logging is defined in the '/logging' subbranch of
+the main property tree; under '/logging', each '/log' subbranch
+defines a separate log with its own output file and interval. Here is
+a simple example that logs the rudder and aileron settings every
+second (1000ms) to the file steering.csv:
+
+
+
+ steering.csv
+ 1000
+
+ Rudder
+ /controls/rudder
+
+
+ Ailerons
+ /controls/aileron
+
+
+
+
+Each 'log' subbranch contains an optional 'filename' property
+(defaults to "fg_log.csv"), an optional 'interval-ms' property
+(defaults to 0, which logs every frame), and a series of 'entry'
+subbranches.
+
+Each 'entry' subbranch contains a 'property' property specifying the
+name of the property to be logged, and an optional 'title' property
+specifying the title to use in the CSV file (defaults to the full path
+of the property). The elapsed time in milliseconds since the start of
+the simulation is always included as the first entry with the title
+"Time", so there is no need to include it explicitly.
+
+Here's a sample of the logging output for the above log:
+
+ Time,Rudder,Ailerons
+ 6522,0.000000,0.000000
+ 7668,-0.000000,0.000000
+ 8702,-0.000000,0.000000
+ 9705,-0.000000,0.000000
+ 10784,-0.000000,0.000000
+ 11792,-0.000000,0.000000
+ 12808,-0.000000,-0.210000
+ 13826,-0.000000,-0.344000
+ 14881,-0.000000,-0.066000
+ 15901,-0.000000,-0.806000
+ 16943,-0.000000,-0.936000
+ 17965,-0.000000,-0.534000
+ 19013,-0.000000,-0.294000
+ 20044,-0.000000,0.270000
+ 21090,-0.000000,-1.000000
+ 22097,-0.000000,-0.168000
+
+Note that the requested interval is only a minimum; most of the time,
+the actual interval is slightly longer than the requested one.
+
+The easiest way for an end-user to define logs is to put the log in a
+separate XML file (usually under the user's home directory), then
+refer to it using the --config option, like this:
+
+ fgfs --config=log-config.xml
+
+The output log files are always relative to the current directory.
+
+--
+
+David Megginson, 2002-03-12
diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am
index ad7df67ed..6324e2245 100644
--- a/src/Main/Makefile.am
+++ b/src/Main/Makefile.am
@@ -47,6 +47,7 @@ fgfs_SOURCES = \
fg_props.cxx fg_props.hxx \
fgfs.cxx fgfs.hxx \
globals.cxx globals.hxx \
+ logger.cxx logger.hxx \
model.cxx model.hxx \
options.cxx options.hxx \
splash.cxx splash.hxx \
diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx
index a78638f0b..375548096 100644
--- a/src/Main/fg_init.cxx
+++ b/src/Main/fg_init.cxx
@@ -116,6 +116,7 @@
#include "fg_props.hxx"
#include "options.hxx"
#include "globals.hxx"
+#include "logger.hxx"
#include "viewmgr.hxx"
#if defined(FX) && defined(XMESA)
@@ -779,6 +780,14 @@ bool fgInitSubsystems( void ) {
fgEVENT::FG_EVENT_READY, 30000 );
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the logger.
+ ////////////////////////////////////////////////////////////////////
+
+ globals->set_logger(new FGLogger);
+ globals->get_logger()->init();
+ globals->get_logger()->bind();
+
////////////////////////////////////////////////////////////////////
// Initialize the local time subsystem.
////////////////////////////////////////////////////////////////////
diff --git a/src/Main/logger.cxx b/src/Main/logger.cxx
new file mode 100644
index 000000000..9dfe7399c
--- /dev/null
+++ b/src/Main/logger.cxx
@@ -0,0 +1,110 @@
+// logger.cxx - log properties.
+// Written by David Megginson, started 2002.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#include "logger.hxx"
+
+#include
+SG_USING_STD(ofstream);
+SG_USING_STD(endl);
+
+#include
+SG_USING_STD(string);
+
+#include
+
+#include "fg_props.hxx"
+
+
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGLogger
+////////////////////////////////////////////////////////////////////////
+
+FGLogger::FGLogger ()
+{
+}
+
+FGLogger::~FGLogger ()
+{
+}
+
+void
+FGLogger::init ()
+{
+ SGPropertyNode * logging = fgGetNode("/logging");
+ if (logging == 0)
+ return;
+
+ vector children = logging->getChildren("log");
+ for (int i = 0; i < children.size(); i++) {
+ _logs.push_back(Log());
+ Log &log = _logs[_logs.size()-1];
+ SGPropertyNode * child = children[i];
+ string filename = child->getStringValue("filename", "fg_log.csv");
+ log.interval_ms = child->getLongValue("interval-ms", 0);
+ log.output = new ofstream(filename.c_str());
+ if (!log.output) {
+ SG_LOG(SG_INPUT, SG_ALERT, "Cannot write log to " << filename);
+ continue;
+ }
+ vector entries = child->getChildren("entry");
+ (*log.output) << "Time";
+ for (int j = 0; j < entries.size(); j++) {
+ SGPropertyNode * entry = entries[j];
+ SGPropertyNode * node =
+ fgGetNode(entry->getStringValue("property"), true);
+ log.nodes.push_back(node);
+ (*log.output) << ','
+ << entry->getStringValue("title", node->getPath());
+ }
+ (*log.output) << endl;
+ }
+}
+
+void
+FGLogger::bind ()
+{
+}
+
+void
+FGLogger::unbind ()
+{
+}
+
+void
+FGLogger::update (int dt)
+{
+ long elapsed_ms = globals->get_elapsed_time_ms();
+ for (int i = 0; i < _logs.size(); i++) {
+ if ((elapsed_ms - _logs[i].last_time_ms) >= _logs[i].interval_ms) {
+ _logs[i].last_time_ms = elapsed_ms;
+ (*_logs[i].output) << globals->get_elapsed_time_ms();
+ for (int j = 0; j < _logs[i].nodes.size(); j++) {
+ (*_logs[i].output) << ',' << _logs[i].nodes[j]->getStringValue();
+ }
+ (*_logs[i].output) << endl;
+ }
+ }
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGLogger::Log
+////////////////////////////////////////////////////////////////////////
+
+FGLogger::Log::Log ()
+ : output(0),
+ interval_ms(0),
+ last_time_ms(-99999999999999L)
+{
+}
+
+FGLogger::Log::~Log ()
+{
+ delete output;
+}
+
+// end of logger.cxx
diff --git a/src/Main/logger.hxx b/src/Main/logger.hxx
new file mode 100644
index 000000000..ef11ef7da
--- /dev/null
+++ b/src/Main/logger.hxx
@@ -0,0 +1,65 @@
+// logger.hxx - log properties.
+// Written by David Megginson, started 2002.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#ifndef __LOGGER_HXX
+#define __LOGGER_HXX 1
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include
+#include
+#include
+#include
+
+#include
+SG_USING_STD(ostream);
+
+#include
+SG_USING_STD(vector);
+
+#include "fgfs.hxx"
+
+
+/**
+ * Log any property values to any number of CSV files.
+ */
+class FGLogger : public FGSubsystem
+{
+public:
+
+ FGLogger ();
+ virtual ~FGLogger ();
+
+ // Implementation of FGSubsystem
+ virtual void init ();
+ virtual void bind ();
+ virtual void unbind ();
+ virtual void update (int dt);
+
+private:
+
+ /**
+ * A single instance of a log file (the logger can contain many).
+ */
+ struct Log {
+ Log ();
+ virtual ~Log ();
+ vector nodes;
+ ostream * output;
+ long interval_ms;
+ long last_time_ms;
+ };
+
+ vector _logs;
+
+};
+
+#endif // __LOGGER_HXX