aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/test/llerror_tut.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/test/llerror_tut.cpp')
-rw-r--r--linden/indra/test/llerror_tut.cpp767
1 files changed, 767 insertions, 0 deletions
diff --git a/linden/indra/test/llerror_tut.cpp b/linden/indra/test/llerror_tut.cpp
new file mode 100644
index 0000000..a4b4258
--- /dev/null
+++ b/linden/indra/test/llerror_tut.cpp
@@ -0,0 +1,767 @@
1/**
2 * @file llerror_tut.cpp
3 * @date December 2006
4 * @brief error unit tests
5 *
6 * Copyright (c) 2006-2007, Linden Research, Inc.
7 *
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "llerror.h"
30namespace
31{
32 void test_that_error_h_includes_enough_things_to_compile_a_message()
33 {
34 llinfos << "!" << llendl;
35 }
36}
37
38#include <tut/tut.h>
39#include "lltut.h"
40
41#include <vector>
42
43#include "llerrorcontrol.h"
44#include "llsd.h"
45
46namespace
47{
48 static bool fatalWasCalled;
49 void fatalCall(const std::string&) { fatalWasCalled = true; }
50
51 class TestRecorder : public LLError::Recorder
52 {
53 public:
54 TestRecorder() : mWantsTime(false) { }
55 ~TestRecorder() { LLError::removeRecorder(this); }
56
57 void recordMessage(LLError::ELevel level,
58 const std::string& message)
59 {
60 mMessages.push_back(message);
61 }
62
63 int countMessages() { return (int) mMessages.size(); }
64 void clearMessages() { mMessages.clear(); }
65
66 void setWantsTime(bool t) { mWantsTime = t; }
67 bool wantsTime() { return mWantsTime; }
68
69 std::string message(int n)
70 {
71 std::ostringstream test_name;
72 test_name << "testing message " << n << ", not enough messages";
73
74 tut::ensure(test_name.str(), n < countMessages());
75 return mMessages[n];
76 }
77
78 private:
79 typedef std::vector<std::string> MessageVector;
80 MessageVector mMessages;
81
82 bool mWantsTime;
83 };
84}
85
86namespace tut
87{
88 struct ErrorTestData
89 {
90 TestRecorder mRecorder;
91 LLError::Settings* mPriorErrorSettings;
92
93 ErrorTestData()
94 {
95 fatalWasCalled = false;
96
97 mPriorErrorSettings = LLError::saveAndResetSettings();
98 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
99 LLError::setFatalFunction(fatalCall);
100 LLError::addRecorder(&mRecorder);
101 }
102
103 ~ErrorTestData()
104 {
105 LLError::removeRecorder(&mRecorder);
106 LLError::restoreSettings(mPriorErrorSettings);
107 }
108
109 void ensure_message_count(int expectedCount)
110 {
111 ensure_equals("message count", mRecorder.countMessages(), expectedCount);
112 }
113
114 void ensure_message_contains(int n, const std::string& expectedText)
115 {
116 std::ostringstream test_name;
117 test_name << "testing message " << n;
118
119 ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
120 }
121
122 void ensure_message_does_not_contain(int n, const std::string& expectedText)
123 {
124 std::ostringstream test_name;
125 test_name << "testing message " << n;
126
127 ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
128 }
129 };
130
131 typedef test_group<ErrorTestData> ErrorTestGroup;
132 typedef ErrorTestGroup::object ErrorTestObject;
133
134 ErrorTestGroup errorTestGroup("error");
135
136 template<> template<>
137 void ErrorTestObject::test<1>()
138 // basic test of output
139 {
140 llinfos << "test" << llendl;
141 llinfos << "bob" << llendl;
142
143 ensure_message_contains(0, "test");
144 ensure_message_contains(1, "bob");
145 }
146}
147
148namespace
149{
150 void writeSome()
151 {
152 lldebugs << "one" << llendl;
153 llinfos << "two" << llendl;
154 llwarns << "three" << llendl;
155 llerrs << "four" << llendl;
156 // fatal messages write out and addtional "error" message
157 }
158};
159
160namespace tut
161{
162 template<> template<>
163 void ErrorTestObject::test<2>()
164 // messages are filtered based on default level
165 {
166 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
167 writeSome();
168 ensure_message_contains(0, "one");
169 ensure_message_contains(1, "two");
170 ensure_message_contains(2, "three");
171 ensure_message_contains(3, "error");
172 ensure_message_contains(4, "four");
173 ensure_message_count(5);
174
175 LLError::setDefaultLevel(LLError::LEVEL_INFO);
176 writeSome();
177 ensure_message_contains(5, "two");
178 ensure_message_contains(6, "three");
179 ensure_message_contains(7, "error");
180 ensure_message_contains(8, "four");
181 ensure_message_count(9);
182
183 LLError::setDefaultLevel(LLError::LEVEL_WARN);
184 writeSome();
185 ensure_message_contains(9, "three");
186 ensure_message_contains(10, "error");
187 ensure_message_contains(11, "four");
188 ensure_message_count(12);
189
190 LLError::setDefaultLevel(LLError::LEVEL_ERROR);
191 writeSome();
192 ensure_message_contains(12, "error");
193 ensure_message_contains(13, "four");
194 ensure_message_count(14);
195
196 LLError::setDefaultLevel(LLError::LEVEL_NONE);
197 writeSome();
198 ensure_message_count(14);
199 }
200
201 template<> template<>
202 void ErrorTestObject::test<3>()
203 // error type string in output
204 {
205 writeSome();
206 ensure_message_contains(0, "DEBUG: ");
207 ensure_message_contains(1, "INFO: ");
208 ensure_message_contains(2, "WARNING: ");
209 ensure_message_does_not_contain(3, "ERROR");
210 ensure_message_contains(4, "ERROR: ");
211 ensure_message_count(5);
212 }
213
214 template<> template<>
215 void ErrorTestObject::test<4>()
216 // file abbreviation
217 {
218 std::string thisFile = __FILE__;
219 std::string abbreviateFile = LLError::abbreviateFile(thisFile);
220
221 ensure_ends_with("file name abbreviation",
222 abbreviateFile,
223 "test/llerror_tut.cpp"
224 );
225 ensure_does_not_contain("file name abbreviation",
226 abbreviateFile, "indra");
227
228 std::string someFile =
229#if LL_WINDOWS
230 "C:/amy/bob/cam.cpp"
231#else
232 "/amy/bob/cam.cpp"
233#endif
234 ;
235 std::string someAbbreviation = LLError::abbreviateFile(someFile);
236
237 ensure_equals("non-indra file abbreviation",
238 someAbbreviation, someFile);
239 }
240}
241
242namespace
243{
244 std::string locationString(int line)
245 {
246 std::ostringstream location;
247 location << LLError::abbreviateFile(__FILE__)
248 << "(" << line << ") : ";
249
250 return location.str();
251 }
252
253 std::string writeReturningLocation()
254 {
255 llinfos << "apple" << llendl; int this_line = __LINE__;
256 return locationString(this_line);
257 }
258
259 std::string writeReturningLocationAndFunction()
260 {
261 llinfos << "apple" << llendl; int this_line = __LINE__;
262 return locationString(this_line) + __FUNCTION__;
263 }
264
265 std::string errorReturningLocation()
266 {
267 llerrs << "die" << llendl; int this_line = __LINE__;
268 return locationString(this_line);
269 }
270}
271
272namespace tut
273{
274 template<> template<>
275 void ErrorTestObject::test<5>()
276 // file and line information in log messages
277 {
278 std::string location = writeReturningLocation();
279 // expecting default to not print location information
280
281 LLError::setPrintLocation(true);
282 writeReturningLocation();
283
284 LLError::setPrintLocation(false);
285 writeReturningLocation();
286
287 ensure_message_does_not_contain(0, location);
288 ensure_message_contains(1, location);
289 ensure_message_does_not_contain(2, location);
290 }
291}
292
293/* The following helper functions and class members all log a simple message
294 from some particular function scope. Each function takes a bool argument
295 that indicates if it should log its own name or not (in the manner that
296 existing log messages often do.) The functions all return their C++
297 name so that test can be substantial mechanized.
298 */
299
300std::string logFromGlobal(bool id)
301{
302 llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
303 return "logFromGlobal";
304}
305
306static std::string logFromStatic(bool id)
307{
308 llinfos << (id ? "logFromStatic: " : "") << "hi" << llendl;
309 return "logFromStatic";
310}
311
312namespace
313{
314 std::string logFromAnon(bool id)
315 {
316 llinfos << (id ? "logFromAnon: " : "") << "hi" << llendl;
317 return "logFromAnon";
318 }
319}
320
321namespace Foo {
322 std::string logFromNamespace(bool id)
323 {
324 llinfos << (id ? "Foo::logFromNamespace: " : "") << "hi" << llendl;
325 //return "Foo::logFromNamespace";
326 // there is no standard way to get the namespace name, hence
327 // we won't be testing for it
328 return "logFromNamespace";
329 }
330}
331
332namespace
333{
334 class ClassWithNoLogType {
335 public:
336 std::string logFromMember(bool id)
337 {
338 llinfos << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << llendl;
339 return "ClassWithNoLogType::logFromMember";
340 }
341 static std::string logFromStatic(bool id)
342 {
343 llinfos << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << llendl;
344 return "ClassWithNoLogType::logFromStatic";
345 }
346 };
347
348 class ClassWithLogType {
349 LOG_CLASS(ClassWithLogType);
350 public:
351 std::string logFromMember(bool id)
352 {
353 llinfos << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << llendl;
354 return "ClassWithLogType::logFromMember";
355 }
356 static std::string logFromStatic(bool id)
357 {
358 llinfos << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << llendl;
359 return "ClassWithLogType::logFromStatic";
360 }
361 };
362
363 std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
364 std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
365 std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
366 std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
367 std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
368
369 void ensure_has_once(const std::string& message,
370 const std::string& actual, const std::string& expected)
371 {
372 std::string::size_type n1 = actual.find(expected);
373 std::string::size_type n2 = std::string::npos;
374 if (n1 != std::string::npos)
375 {
376 n2 = std::string(actual, n1 + expected.size()).find(expected);
377 }
378
379 if (n1 == std::string::npos || n2 != std::string::npos)
380 {
381 std::stringstream ss;
382 ss << message << ": " << "expected to find one copy of " << expected
383 << " in actual " << actual;
384 throw tut::failure(ss.str().c_str());
385 }
386 }
387
388 typedef std::string (*LogFromFunction)(bool);
389 void testLogNameOnce(TestRecorder& recorder, LogFromFunction f,
390 const std::string& class_name = "")
391 {
392 recorder.clearMessages();
393 std::string name = f(false);
394 f(true);
395
396 std::string messageWithoutName = recorder.message(0);
397 std::string messageWithName = recorder.message(1);
398
399 ensure_has_once(name + " logged without name",
400 messageWithoutName, name);
401 ensure_has_once(name + " logged with name",
402 messageWithName, name);
403
404 if (!class_name.empty())
405 {
406 ensure_has_once(name + "logged without name",
407 messageWithoutName, class_name);
408 ensure_has_once(name + "logged with name",
409 messageWithName, class_name);
410 }
411 }
412}
413
414namespace tut
415{
416 template<> template<>
417 // class/function information in output
418 void ErrorTestObject::test<6>()
419 {
420 testLogNameOnce(mRecorder, logFromGlobal);
421 testLogNameOnce(mRecorder, logFromStatic);
422 testLogNameOnce(mRecorder, logFromAnon);
423 testLogNameOnce(mRecorder, logFromNamespace);
424 //testLogNameOnce(mRecorder, logFromClassWithNoLogTypeMember, "ClassWithNoLogType");
425 //testLogNameOnce(mRecorder, logFromClassWithNoLogTypeStatic, "ClassWithNoLogType");
426 // XXX: figure out what the exepcted response is for these
427 testLogNameOnce(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType");
428 testLogNameOnce(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType");
429 }
430}
431
432namespace
433{
434 std::string innerLogger()
435 {
436 llinfos << "inside" << llendl;
437 return "moo";
438 }
439
440 std::string outerLogger()
441 {
442 llinfos << "outside(" << innerLogger() << ")" << llendl;
443 return "bar";
444 }
445
446 void uberLogger()
447 {
448 llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
449 }
450
451 class LogWhileLogging
452 {
453 public:
454 void print(std::ostream& out) const
455 {
456 llinfos << "logging" << llendl;
457 out << "baz";
458 }
459 };
460
461 std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l)
462 { l.print(out); return out; }
463
464 void metaLogger()
465 {
466 LogWhileLogging l;
467 llinfos << "meta(" << l << ")" << llendl;
468 }
469
470}
471
472namespace tut
473{
474 template<> template<>
475 // handle nested logging
476 void ErrorTestObject::test<7>()
477 {
478 outerLogger();
479 ensure_message_contains(0, "inside");
480 ensure_message_contains(1, "outside(moo)");
481 ensure_message_count(2);
482
483 uberLogger();
484 ensure_message_contains(2, "inside");
485 ensure_message_contains(3, "inside");
486 ensure_message_contains(4, "outside(moo)");
487 ensure_message_contains(5, "uber(bar,moo)");
488 ensure_message_count(6);
489
490 metaLogger();
491 ensure_message_contains(6, "logging");
492 ensure_message_contains(7, "meta(baz)");
493 ensure_message_count(8);
494 }
495
496 template<> template<>
497 // special handling of llerrs calls
498 void ErrorTestObject::test<8>()
499 {
500 LLError::setPrintLocation(false);
501 std::string location = errorReturningLocation();
502
503 ensure_message_contains(0, location + "error");
504 ensure_message_contains(1, "die");
505 ensure_message_count(2);
506
507 ensure("fatal callback called", fatalWasCalled);
508 }
509}
510
511namespace
512{
513 std::string roswell()
514 {
515 return "1947-07-08T03:04:05Z";
516 }
517
518 void ufoSighting()
519 {
520 llinfos << "ufo" << llendl;
521 }
522}
523
524namespace tut
525{
526 template<> template<>
527 // time in output (for recorders that need it)
528 void ErrorTestObject::test<9>()
529 {
530 LLError::setTimeFunction(roswell);
531
532 mRecorder.setWantsTime(false);
533 ufoSighting();
534 ensure_message_contains(0, "ufo");
535 ensure_message_does_not_contain(0, roswell());
536
537 mRecorder.setWantsTime(true);
538 ufoSighting();
539 ensure_message_contains(1, "ufo");
540 ensure_message_contains(1, roswell());
541 }
542
543 template<> template<>
544 // output order
545 void ErrorTestObject::test<10>()
546 {
547 LLError::setPrintLocation(true);
548 LLError::setTimeFunction(roswell);
549 mRecorder.setWantsTime(true);
550 std::string locationAndFunction = writeReturningLocationAndFunction();
551
552 ensure_equals("order is time type location function message",
553 mRecorder.message(0),
554 roswell() + " INFO: " + locationAndFunction + ": apple");
555 }
556
557 template<> template<>
558 // multiple recorders
559 void ErrorTestObject::test<11>()
560 {
561 TestRecorder altRecorder;
562 LLError::addRecorder(&altRecorder);
563
564 llinfos << "boo" << llendl;
565
566 ensure_message_contains(0, "boo");
567 ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
568 ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
569
570 LLError::setTimeFunction(roswell);
571
572 TestRecorder anotherRecorder;
573 anotherRecorder.setWantsTime(true);
574 LLError::addRecorder(&anotherRecorder);
575
576 llinfos << "baz" << llendl;
577
578 std::string when = roswell();
579
580 ensure_message_does_not_contain(1, when);
581 ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
582 ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
583 ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
584 ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
585 }
586}
587
588class TestAlpha
589{
590 LOG_CLASS(TestAlpha);
591public:
592 static void doDebug() { lldebugs << "add dice" << llendl; }
593 static void doInfo() { llinfos << "any idea" << llendl; }
594 static void doWarn() { llwarns << "aim west" << llendl; }
595 static void doError() { llerrs << "ate eels" << llendl; }
596 static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
597};
598
599class TestBeta
600{
601 LOG_CLASS(TestBeta);
602public:
603 static void doDebug() { lldebugs << "bed down" << llendl; }
604 static void doInfo() { llinfos << "buy iron" << llendl; }
605 static void doWarn() { llwarns << "bad word" << llendl; }
606 static void doError() { llerrs << "big easy" << llendl; }
607 static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
608};
609
610namespace tut
611{
612 template<> template<>
613 // filtering by class
614 void ErrorTestObject::test<12>()
615 {
616 LLError::setDefaultLevel(LLError::LEVEL_WARN);
617 LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
618
619 TestAlpha::doAll();
620 TestBeta::doAll();
621
622 ensure_message_contains(0, "aim west");
623 ensure_message_contains(1, "error");
624 ensure_message_contains(2, "ate eels");
625 ensure_message_contains(3, "buy iron");
626 ensure_message_contains(4, "bad word");
627 ensure_message_contains(5, "error");
628 ensure_message_contains(6, "big easy");
629 ensure_message_count(7);
630 }
631
632 template<> template<>
633 // filtering by function, and that it will override class filtering
634 void ErrorTestObject::test<13>()
635 {
636 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
637 LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
638 LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
639 LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
640
641 TestBeta::doAll();
642 ensure_message_contains(0, "buy iron");
643 ensure_message_contains(1, "bad word");
644 ensure_message_count(2);
645 }
646
647 template<> template<>
648 // filtering by file
649 // and that it is overridden by both class and function filtering
650 void ErrorTestObject::test<14>()
651 {
652 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
653 LLError::setFileLevel(LLError::abbreviateFile(__FILE__),
654 LLError::LEVEL_WARN);
655 LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO);
656 LLError::setFunctionLevel("TestAlpha::doError",
657 LLError::LEVEL_NONE);
658 LLError::setFunctionLevel("TestBeta::doError",
659 LLError::LEVEL_NONE);
660
661 TestAlpha::doAll();
662 TestBeta::doAll();
663 ensure_message_contains(0, "any idea");
664 ensure_message_contains(1, "aim west");
665 ensure_message_contains(2, "bad word");
666 ensure_message_count(3);
667 }
668
669 template<> template<>
670 // proper cached, efficient lookup of filtering
671 void ErrorTestObject::test<15>()
672 {
673 LLError::setDefaultLevel(LLError::LEVEL_NONE);
674
675 TestAlpha::doInfo();
676 ensure_message_count(0);
677 ensure_equals("first check", LLError::shouldLogCallCount(), 1);
678 TestAlpha::doInfo();
679 ensure_message_count(0);
680 ensure_equals("second check", LLError::shouldLogCallCount(), 1);
681
682 LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
683 TestAlpha::doInfo();
684 ensure_message_count(1);
685 ensure_equals("third check", LLError::shouldLogCallCount(), 2);
686 TestAlpha::doInfo();
687 ensure_message_count(2);
688 ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
689
690 LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
691 TestAlpha::doInfo();
692 ensure_message_count(2);
693 ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
694 TestAlpha::doInfo();
695 ensure_message_count(2);
696 ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
697 }
698
699 template<> template<>
700 // configuration from LLSD
701 void ErrorTestObject::test<16>()
702 {
703 std::string this_file = LLError::abbreviateFile(__FILE__);
704 LLSD config;
705 config["print-location"] = true;
706 config["default-level"] = "DEBUG";
707
708 LLSD set1;
709 set1["level"] = "WARN";
710 set1["files"][0] = this_file;
711
712 LLSD set2;
713 set2["level"] = "INFO";
714 set2["classes"][0] = "TestAlpha";
715
716 LLSD set3;
717 set3["level"] = "NONE";
718 set3["functions"][0] = "TestAlpha::doError";
719 set3["functions"][1] = "TestBeta::doError";
720
721 config["settings"][0] = set1;
722 config["settings"][1] = set2;
723 config["settings"][2] = set3;
724
725 LLError::configure(config);
726
727 TestAlpha::doAll();
728 TestBeta::doAll();
729 ensure_message_contains(0, "any idea");
730 ensure_message_contains(0, this_file);
731 ensure_message_contains(1, "aim west");
732 ensure_message_contains(2, "bad word");
733 ensure_message_count(3);
734
735 // make sure reconfiguring works
736 LLSD config2;
737 config2["default-level"] = "WARN";
738
739 LLError::configure(config2);
740
741 TestAlpha::doAll();
742 TestBeta::doAll();
743 ensure_message_contains(3, "aim west");
744 ensure_message_does_not_contain(3, this_file);
745 ensure_message_contains(4, "error");
746 ensure_message_contains(5, "ate eels");
747 ensure_message_contains(6, "bad word");
748 ensure_message_contains(7, "error");
749 ensure_message_contains(8, "big easy");
750 ensure_message_count(9);
751 }
752}
753
754/* Tests left:
755 handling of classes without LOG_CLASS
756
757 live update of filtering from file
758
759 syslog recorder
760 file recorder
761 cerr/stderr recorder
762 fixed buffer recorder
763 windows recorder
764
765 mutex use when logging (?)
766 strange careful about to crash handling (?)
767*/