I would like to configure an advance logger using poco and its configuration file.
I create a config.xml file like that :
<?xml version="1.0" ?>
<Application>
<logging>
<channels>
<c1>
<class>ColorConsoleChannel</class>
<formatter>
<class>PatternFormatter</class>
<pattern>%Y-%m-%d %H:%M:%S : %s : [%p] : %t</pattern>
</formatter>
<traceColor>lightBlue</traceColor>
<debugColor>blue</debugColor>
<informationColor>green</informationColor>
<noticeColor>green</noticeColor>
<warningColor>yellow</warningColor>
<errorColor>red</errorColor>
<criticalColor>lightMagenta</criticalColor>
<fatalColor>lightMagenta</fatalColor>
</c1>
<c2>
<class>FileChannel</class>
<path>logs/traceApplication.log</path>
<rotation>1 M</rotation>
<archive>number</archive>
<purgeCount>5</purgeCount>
<formatter>
<class>PatternFormatter</class>
<pattern>%Y-%m-%d %H:%M:%S : %T : [%p] : %t</pattern>
</formatter>
</c2>
</channels>
<loggers>
<consoleLogger>
<channel>c1</channel>
<level>information</level>
</consoleLogger>
<traceFileLogger>
<channel>c2</channel>
<level>trace</level>
</traceFileLogger>
</loggers>
<channels>
<cSplitter>
<class>SplitterChannel</class>
<channels>consoleLogger,traceFileLogger,mainFileLogger</channels>
</cSplitter>
</channels>
<loggers>
<root>
<channel>cSplitter</channel>
<level>trace</level>
</root>
</loggers>
</logging>
</Application>
I use a Poco::Util::ServerApplication class and in the initialize method I put :
void CBS2AudioVideo::initialize(Poco::Util::Application& self)
{
loadConfiguration("config.xml");
Poco::Util::ServerApplication::initialize(self);
}
Before adding the splitterChannel my logging works well but with it, it doesn't any more.
I got the error message :
Not found: logging channel: consoleLogger
My goal is to have only one root logger and when I use it, it log in information level into the console and in trace level into the file.
When I set channels in channels.cSplitter.channels it works but all channels are logged to the same level. And if I take the logging configuration slide (http://pocoproject.org/slides/185-LoggingConfiguration.pdf) they use loggers and not channels in the logging.channels.splitter.channels attribute area. So I think it is possible. More over the Logger class inherit from Channel too.
Someone has already done this kind of work or have an idea ?
I've finally found the solution.
I post it here if it interest someone.
This file works well.
<?xml version="1.0" ?>
<Application>
<logging>
<channels>
<cScreen>
<class>ColorConsoleChannel</class>
<formatter>
<class>PatternFormatter</class>
<pattern>%H:%M:%S : %T : [%p] : %t</pattern>
</formatter>
<traceColor>lightBlue</traceColor>
<debugColor>blue</debugColor>
<informationColor>white</informationColor>
<noticeColor>green</noticeColor>
<warningColor>yellow</warningColor>
<errorColor>red</errorColor>
<criticalColor>lightMagenta</criticalColor>
<fatalColor>lightMagenta</fatalColor>
</cScreen>
<cFile>
<class>FileChannel</class>
<path>logs/application.log</path>
<rotation>1 M</rotation>
<archive>number</archive>
<purgeCount>5</purgeCount>
<formatter>
<class>PatternFormatter</class>
<pattern>%H:%M:%S : %T : [%p] : %t</pattern>
</formatter>
</cFile>
</channels>
<loggers>
<root>
<name></name>
<channel>cFile</channel>
<level>trace</level>
</root>
<main>
<name>main</name>
<channel>cScreen</channel>
<level>trace</level>
</main>
</loggers>
</logging>
To log on the screen and in the file make :
Poco::Logger::get("main").trace(msg);
To log only into file make
Poco::Logger::get("").trace(msg);
Related
I am working on writing rules using Schematron to validate data below. The requirement is to verify whether a patient has at least one encounter in the past 12 months. If there are multiple encounters per patient, use the last encounter.
<root>
<entry>
<resource>
<resourceType>Encounter</resourceType>
<subject>
<id>Patient/12345</id>
</subject>
<encounterDate>2018-04-10T10:00:00</encounterDate>
</resource>
</entry>
<entry>
<resource>
<resourceType>Encounter</resourceType>
<subject>
<id>Patient/abcde</id>
</subject>
<encounterDate>2020-04-10T10:00:00</encounterDate>
</resource>
</entry>
<entry>
<resource>
<resourceType>Encounter</resourceType>
<subject>
<id>Patient/abcde</id>
</subject>
<encounterDate>2019-05-10T10:00:00</encounterDate>
</resource>
</entry>
</root>
The above data should pass the validation because the latest encounter is less than a year ago.
What I want to know is, if I write a template that groups encounters together by patient id, is there a way to pass that template to the rule context? If not, is there any other way of doing it?
I am completely new to both xslt and Schematron and here is what I have so far:
<schema xmlns="http://purl.oclc.org/dsdl/schematron" >
<pattern>
<key name="patientId" match="entry" use="/resouce/subject/id/text()"/>
<template name="dateByPatient" match="entry">
<root>
<for-each select="resource/subject/id">
<patient >
<for-each select="key('patientId',text())">
<effectiveDateTime><value-of select="./resource/encounterDate"/></effectiveDateTime>
</for-each>
</patient>
</for-each>
</root>
</template>
<let name="template">
<dateByPatient/>
</let>
<let name="latest">
<root>
<for-each select="$template/root/patient">
<patient >
<sort select="effectiveDateTime" order="descending" />
<if test="position() = 1">
<effectiveDateTime><value-of select="effectiveDateTime" /></effectiveDateTime>
</if>
</patient>
</for-each>
</root>
</let>
<rule context="$latest/root/patient/effectiveDateTime">
<let name="days" value="days-from-duration(fn:current-dateTime() - xs:dateTime(text()))" />
<assert test="days-from-duration(fn:current-dateTime() - xs:dateTime(text())) < 365">
Encounter date more than a year : <value-of select="$days" /> days
</assert>
</rule>
</pattern>
</schema>
With XSLT 3 underlying you could use
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
<sch:ns prefix="map" uri="http://www.w3.org/2005/xpath-functions/map"/>
<sch:pattern>
<sch:rule context="root">
<sch:let name="groups"
value="let $encounter-resources := entry/resource[resourceType = 'Encounter']
return map:merge(
$encounter-resources
!
map {
data(subject/id) : xs:dateTime(encounterDate)
},
map { 'duplicates' : 'combine' }
)"/>
<sch:assert
test="every $patient in map:keys($groups)
satisfies
(current-dateTime() - max($groups($patient)))
lt xs:dayTimeDuration('P365D')">At least one patient with latest encounter more than a year ago.</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>
Or to output more detailed information and to only process resources with type Encounter:
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
<sch:ns prefix="map" uri="http://www.w3.org/2005/xpath-functions/map"/>
<sch:pattern>
<sch:rule context="root">
<sch:let name="groups"
value="let $encounter-resources := entry/resource[resourceType = 'Encounter']
return map:merge(
$encounter-resources
!
map {
data(subject/id) : xs:dateTime(encounterDate)
},
map { 'duplicates' : 'combine' }
)"/>
<sch:let name="failing-patients"
value="map:keys($groups)[(current-dateTime() - max($groups(.))) gt xs:dayTimeDuration('P365D')]"/>
<sch:report
test="exists($failing-patients)">Patients <sch:value-of select="$failing-patients"/> with latest encounter more than a year ago.</sch:report>
</sch:rule>
</sch:pattern>
</sch:schema>
I don't think you can mix Schematron and XSLT as freely as your code tries, you would need to set up an XProc pipeline to use p:xslt to group the original input and then a validation step to validate with Schematron.
As for your problems to run the second sample with node-schematron, it uses an XPath implementation that doesn't support the XPath 3.1 sort function it seems, node-schematron also fails to handle maps as intermediary results of a Schematron variable, so only stuffing all into one variable expression seems to do; two examples work:
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
<sch:ns prefix="map" uri="http://www.w3.org/2005/xpath-functions/map"/>
<sch:pattern>
<sch:rule context="root">
<sch:let name="failing-patients"
value="let $encounter-resources := entry/resource[resourceType = 'Encounter'],
$groups := map:merge(
$encounter-resources
!
map {
data(subject/id) : xs:dateTime(encounterDate)
},
map { 'duplicates' : 'combine' }
)
return map:keys($groups)[(current-dateTime() - max($groups(.))) gt xs:dayTimeDuration('P365D')]"/>
<sch:report
test="exists($failing-patients)">Patients <sch:value-of select="$failing-patients"/> with latest encounter more than a year ago.</sch:report>
</sch:rule>
</sch:pattern>
</sch:schema>
or
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
<sch:ns prefix="map" uri="http://www.w3.org/2005/xpath-functions/map"/>
<sch:pattern>
<sch:rule context="root">
<sch:let name="failing-patients"
value="let
$encounter-resources := entry/resource[resourceType = 'Encounter'],
$groups := fold-left(
$encounter-resources,
map{},
function($m, $e) {
map:put(
$m,
data($e/subject/id),
max((xs:dateTime($e/encounterDate), map:get($m, data($e/subject/id))))
)
})
return map:keys($groups)[(current-dateTime() - $groups(.)) gt xs:dayTimeDuration('P365D')]"/>
<sch:report test="exists($failing-patients)">Patients <sch:value-of
select="$failing-patients"/> with latest encounter more than a year
ago.</sch:report>
</sch:rule>
</sch:pattern>
</sch:schema>
If you need an assertion that fails then replace the sch:report with
<sch:assert
test="empty($failing-patients)">Patients <sch:value-of select="$failing-patients"/> with latest encounter more than a year ago.</sch:assert>
Jetty 9.3.8.v20160314 running on AWS EC2 Linux machine with Java 1.8.0_51 has two web apps under two different dir+context.xml - webapps/app1 and webapps/app2 with an empty webapps/ROOT directory.
I'm trying to get each webapp to have a separate log with contextPath in its name.
Each app has the following webapps/appX.xml context defined -
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/appX</Set>
<Set name="war">
<SystemProperty name="jetty.home" default="."/>/webapps/appX
</Set>
</Configure>
Followed Example: Centralized Logging with Logback and installed webapp-logging module, app is using slf4j Logger and resources/logback.xml is -
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n</pattern>
</encoder>
</appender>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>contextPath</key>
<defaultValue>unknown</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${contextPath}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${jetty.home}/logs/${contextPath}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${contextPath}_%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n</pattern>
</encoder>
<append>true</append>
</appender>
</sift>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="SIFT"/>
</root>
</configuration>
But it all goes to unknown.log. Using servername instead works well and creates separate file for each domain being used to access the server.
So it would seem that contextPath isn't properly assigned by ContextLogHandler.
Why is it empty?
I have run the my unit tests written with the google test 1.6.0 framework with the --gtest_output="xml:test-results.xml" flag and get a test result file like this:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="8" failures="0" disabled="0" errors="0" time="81.396" name="AllTests">
<testsuite name="TestSuite1" tests="8" failures="0" disabled="0" errors="0" time="81.396">
<testcase name="Test1" status="run" time="6.391" classname="Class1" />
<testcase name="Test2" status="run" time="6.1" classname="Class1" />
<testcase name="Test3" status="run" time="7.146" classname="Class1" />
<testcase name="Test4" status="run" time="16.164" classname="Class1" />
<testcase name="Test5" status="run" time="25.145" classname="Class1" />
<testcase name="Test6" status="run" time="6.099" classname="Class1" />
<testcase name="Test7" status="run" time="6.162" classname="Class1" />
<testcase name="Test8" status="run" time="8.187" classname="Class1" />
</testsuite>
</testsuites>
Based on what I have read in other posts the gtest xml output is supposed to be compatible with junit parsers. Related Post: Unable to get hudson to parse JUnit test output XML
The other possibility of error is in my bitten script. Running on Trac 0.12.2. Here is my bitten recipe for parsing the XML file using the java:junit parser:
<build xmlns:java="http://bitten.edgewall.org/tools/java">
<step id="parse_results" description="Gather Results" onerror="fail">
<java:junit file="/home/user/temp/test-results.xml" />
</step>
</build>
In trac, it says that the build was successful, but the test results are blank. 0 run, 0 failed, 0 ignored, 0 errors
Thanks.
I was able to solve the problem. It turns out that Trac's JUnit parser has a bug. It doesn't like the testsuites tag and it doesn't like having multiple testsuite sections. PHP allows the testsuites tag, but will not do multiple files. I chose to create a parser in Python that creates multiple XML files from the Gtest output file.
def move_results(results, results_dir):
# Moves all results into a temp folder to be consumed by Bitten
# Files with multiple test suite sections, split into individual files
for files in results:
fin = open(files)
test_id = 0
split_line = files.split('/')
file_name = split_line[len(split_line)-1].split('.xml')
for line in fin:
if not 'testsuites' in line:
if '<testsuite ' in line:
output_file = results_dir + file_name[0] + '-' + str(test_id) + '.xml'
test_id = test_id + 1
fout = open(output_file, 'w')
fout.write('<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n')
fout.write(line)
elif '<testsuite\\>' in line:
fout.write(line)
fout.close()
elif not '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n' in line:
fout.write(line)
fin.close()
os.remove(files)
Alternatively, you could use Bitten to do convert using an XSLT. Also you could combine the output files into a single output (with something like nose's XUnit output) and use php:phpunit to parse the file.
I develop a syncml server and I do not synchronize from the server to the client (nokia e71) for modified contacts. All the rest works except when I make a command replace towards the client for an existing localuid. The client returns me then the status 415 for this command (type or format of the datum is not corresponding) while the customer accepts the same datum for an addition (by a command add or replaces).
Has anybody already met this problem ?
Here are messages sent between the client and the server:
Server message with Replace command:
<?xml version="1.0" ?>
<!DOCTYPE SyncML
PUBLIC "-//SYNCML//DTD SyncML 1.2//EN"
"http://www.openmobilealliance.org/tech/DTD/OMA-TS-SyncML_RepPro_DTD-V1_2.dtd">
<SyncML xmlns="SYNCML:SYNCML1.2">
<SyncHdr>
<VerDTD>1.2</VerDTD><VerProto>SyncML/1.2</VerProto><SessionID>235</SessionID><MsgID>3</MsgID> <Target><LocURI>IMEI:358240030276208</LocURI></Target> <Source><LocURI>http://192.168.8.20:50000</LocURI></Source> <Meta>
<MaxMsgSize xmlns="syncml:metinf">1000000</MaxMsgSize><MaxObjSize xmlns="syncml:metinf">4000000</MaxObjSize>
</Meta>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>1</CmdID><MsgRef>3</MsgRef><CmdRef>0</CmdRef><Cmd>SyncHdr</Cmd> <TargetRef>http://192.168.8.20:50000</TargetRef> <SourceRef>IMEI:358240030276208</SourceRef> <Data>200</Data>
</Status> <Status>
<CmdID>2</CmdID><MsgRef>3</MsgRef><CmdRef>3</CmdRef><Cmd>Sync</Cmd> <SourceRef>./C:Contacts.cdb</SourceRef> <TargetRef>./card</TargetRef> <Data>200</Data>
</Status> <Sync>
<CmdID>3</CmdID> <Source><LocURI>./card</LocURI></Source> <Target><LocURI>./C:Contacts.cdb</LocURI></Target><NumberOfChanges>1</NumberOfChanges> <Replace>
<CmdID>4</CmdID> <Meta><Type xmlns="syncml:metinf">text/x-vcard</Type> </Meta> <Item> <Target><LocURI>69</LocURI></Target> <Data>
<![CDATA[BEGIN:VCARD VERSION:2.1 N:Smith;Change;;; FN:Change Smith END:VCARD]]>
</Data> </Item>
</Replace>
</Sync> <Final/>
</SyncBody>
</SyncML>
Client Message with a replace command status:
<?xml version="1.0" ?>
<!DOCTYPE SyncML
PUBLIC '-//SYNCML//DTD SyncML 1.2//EN'
'http://www.openmobilealliance.org/tech/DTD/OMA-TS-SyncML_RepPro_DTD-V1_2.dtd'>
<SyncML xmlns="SYNCML:SYNCML1.2">
<SyncHdr>
<VerDTD>
1.2
</VerDTD>
<VerProto>
SyncML/1.2
</VerProto>
<SessionID>
235
</SessionID>
<MsgID>
4
</MsgID>
<Target>
<LocURI>
http://192.168.8.20:50000
</LocURI>
</Target>
<Source>
<LocURI>
IMEI:358240030276208
</LocURI>
<LocName>
test1
</LocName>
</Source>
<Meta>
<MaxMsgSize xmlns="syncml:metinf">
65535
</MaxMsgSize>
</Meta>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>
1
</CmdID>
<MsgRef>
3
</MsgRef>
<CmdRef>
0
</CmdRef>
<Cmd>
SyncHdr
</Cmd>
<TargetRef>
IMEI:358240030276208
</TargetRef>
<SourceRef>
http://192.168.8.20:50000
</SourceRef>
<Data>
200
</Data>
</Status>
<Status>
<CmdID>
2
</CmdID>
<MsgRef>
3
</MsgRef>
<CmdRef>
3
</CmdRef>
<Cmd>
Sync
</Cmd>
<TargetRef>
./C:Contacts.cdb
</TargetRef>
<SourceRef>
./card
</SourceRef>
<Data>
200
</Data>
</Status>
<Status>
<CmdID>
3
</CmdID>
<MsgRef>
3
</MsgRef>
<CmdRef>
4
</CmdRef>
<Cmd>
Replace
</Cmd>
<TargetRef>
69
</TargetRef>
<Data>
415
</Data>
</Status>
<Final/>
</SyncBody>
</SyncML>
I'm just discovering log4cxx logging framework.
It seems there are two different syntaxes for writing config file:
xml manner
key-value manner
Is there a difference or a best practice in this two approaches?
In log4j, Ceki Gulcu (the author) suggests XML configuration over text file, and it takes precedence in default initialization, too (log4j.xml over log4j.txt). You can achieve slightly more with XML configuration than with the text file (I think you cannot manipulate logger additivity and set log4j debug mode with text file configuration).
That said, log4cxx first looks for log4cxx.xml, too, but there are hardly any examples of configuration on the net (and no official documentation, either), so you'll probably need to analyse the DOMConfigurator source code to find out what's possible (referring to log4j examples may prove misleading, as it's not always exactly the same thing).
To conclude, log4cxx popularity in C++ world does not even come close to log4j's in Java. I wonder why (and what the heck IS popular there, except for tons of ad-hoc solutions).
This isn't actually an answer for the question but when you google for:
log4cxx xml config file syntax
this question is the top search result. As it was mentioned by #MaDa it's difficult to find an XML config file example for log4cxx and syntax description. So this is it. Simplest possible, just to log into console and into a log file.
<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Output log messages to the system console. -->
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %c{1} - %m%n" />
</layout>
</appender>
<!-- Also output log messages to the log file. -->
<appender name="FileAppender" class="org.apache.log4j.FileAppender">
<param name="file" value="LogFile.log" />
<param name="append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p %C{2} (%F:%L) - %m%n" />
</layout>
</appender>
<root>
<priority value="all" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="FileAppender" />
</root>
</log4j:configuration>
And simple usage example:
#include "log4cxx/logger.h"
#include "log4cxx/xml/domconfigurator.h"
using namespace log4cxx;
using namespace log4cxx::xml;
LoggerPtr logger (Logger::getLogger ("TEST"));
int main ()
{
DOMConfigurator::configure ("Log4cxxConfig.xml");
LOG4CXX_INFO (logger, "App started!");
LOG4CXX_ERROR (logger, "Some error!");
return 0;
}