How to add HTTP Headers Logback Logstash - http-headers

I read through the following documentation but cannot figure out what other configuration I need: https://github.com/logstash/logstash-logback-encoder#header-fields
My config file:
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app_name":"${APP_NAME:-N/A}","app_version":"${APP_VERSION:-N/A}","hostname":"${HOST:-N/A}","environment":"${environment:-${ENVIRONMENT:-N/A}}"}</customFields>
<includeContext>false</includeContext>
<timeZone>UTC</timeZone>
</encoder>
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
<fieldNames>
<requestHeaders>request_headers</requestHeaders>
</fieldNames>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_ROTATION:-${LOG_FILE}%d{yyyy-MM-dd}.%i}</fileNamePattern>
<maxHistory>${LOG_FILE_ROTATION_MAX_HISTORY:-1}</maxHistory>
<totalSizeCap>${LOG_FILE_ROTATION_TOTAL_SIZE_CAP:-3GB}</totalSizeCap>
<maxFileSize>${LOG_FILE_ROTATION_MAX_FILE_SIZE:-1GB}</maxFileSize>
</rollingPolicy>
</appender>
<root level="${LOG_LEVEL:-INFO}">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
Error:
java.lang.IllegalStateException: Logback configuration error detected:
ERROR in ch.qos.logback.core.joran.spi.Interpreter#19:29 - no applicable action for [requestHeaders], current ElementPath is [[configuration][appender][encoder][fieldNames][requestHeaders]]

The xml configuration file you provided appears to be for logback-classic (since it includes <root level=...).
Automatic logging of request headers by logstash-logback-encoder is only available for IAccessEvents logged via logback-access.
In other words, automatic logging of request headers is not available for ILoggingEvents logged via a Logger from logback-classic. However, you can manually include them when using logback-classic as described below.
Logging of request headers with logback-access
To log request headers for IAccessEvents logged via logback-access, follow the instructions for setting up logback-access for tomcat or jetty, and add the following to your logback-access.xml. (Note this is not the logback.xml file used by logback-classic)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
<fieldNames>
<requestHeaders>request_headers</requestHeaders>
</fieldNames>
</encoder>
</appender>
<appender-ref ref="console"/>
</configuration>
This configuration was tested using logstash-logback-encoder 5.3. Note that the xml element names are different in logstash-logback-encoder versions prior to 5.0, so ensure you are using 5.0+ with that configuration.
Logging of request headers with logback-classic
logstash-logback-encoder does not provide first-class support for logging request headers via logback-classic. However, you include them in a log event logged via a Logger by using event-specific custom fields.
For example, in a class that has access to the http request (such as a servlet filter), you could do something like this:
Map<String, String> httpHeadersMap = ...; // get http request headers as a map
LOGGER.info("request", StructuredArguments.entries(httpHeadersMap));
and configure a LogbackEncoder in your logback.xml like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>

Related

Apache Ignite Structured Logging

I am looking to enable structured logging for Ignite.
Ignite runs inside a docker container.
I enabled the log4j2 module and added a log4j2 configuration file that tries to use <JsonTemplateLayout.../> as described here but in the logs i get the message:
Console contains an invalid element or attribute "JsonTemplateLayout"
Which is probably caused by not having the log4j-layout-template-json dependency available inside ignite. Is there a way how to add the dependency to Ignite or is there another option on how to get structured logging working?
Ignite configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
...
<property name="gridLogger">
<bean class="org.apache.ignite.logger.log4j2.Log4J2Logger">
<constructor-arg type="java.lang.String" value="config/ignite-log4j2-custom.xml"/>
</bean>
</property>
</bean>
</beans>
log4j2 configuration:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="60" status="debug">
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<!-- <PatternLayout pattern="[%d{ISO8601}][%-5p][%t][%c{1}]%notEmpty{[%markerSimpleName]} %m%n"/> -->
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
<JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
</Console>
<Console name="CONSOLE_ERR" target="SYSTEM_ERR">
<!-- <PatternLayout pattern="[%d{ISO8601}][%-5p][%t][%c{1}]%notEmpty{[%markerSimpleName]} %m%n"/> -->
<JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
</Console>
<File name="CONSISTENCY" fileName="${sys:IGNITE_HOME}/work/log/consistency.log">
<PatternLayout>
<Pattern>"[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"</Pattern>
</PatternLayout>
</File>
<Routing name="FILE">
<Routes pattern="$${sys:nodeId}">
<Route>
<RollingFile name="Rolling-${sys:nodeId}" fileName="${sys:IGNITE_HOME}/work/log/${sys:appId}-${sys:nodeId}.log"
filePattern="${sys:IGNITE_HOME}/work/log/${sys:appId}-${sys:nodeId}-%i-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="[%d{ISO8601}][%-5p][%t][%c{1}]%notEmpty{[%markerSimpleName]} %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="6" modulate="true" />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<!-- <Logger name="org.apache.ignite" level="INFO"/> -->
<!--
Uncomment to disable courtesy notices, such as SPI configuration
consistency warnings.
-->
<!--
<Logger name="org.apache.ignite.CourtesyConfigNotice" level=OFF/>
-->
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.eclipse.jetty" level="WARN"/>
<Logger name="org.apache.ignite.internal.visor.consistency" additivity="false" level="INFO">
<AppenderRef ref="CONSISTENCY"/>
</Logger>
<!--
Avoid warnings about failed bind attempt when multiple nodes running on the same host.
-->
<Logger name="org.eclipse.jetty.util.log" level="ERROR"/>
<Logger name="org.eclipse.jetty.util.component" level="ERROR"/>
<Logger name="com.amazonaws" level="WARN"/>
<Root level="INFO">
<!-- Uncomment to enable logging to console. -->
<AppenderRef ref="CONSOLE" level="INFO"/>
<AppenderRef ref="CONSOLE_ERR" level="ERROR"/>
<AppenderRef ref="FILE" level="DEBUG"/>
</Root>
</Loggers>
</Configuration>
When adding the JAR to libs (as suggested by Stanislav below) i get a step further but also get an error (not a java person so any hint is highly appreciated):
main ERROR An exception occurred processing Appender CONSOLE org.apache.logging.log4j.core.appender.AppenderLoggingException: java.lang.IllegalAccessError: class org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder tried to access method 'void org.apache.logging.log4j.core.layout.TextEncoderHelper.encodeText(java.nio.charset.CharsetEncoder, java.nio.CharBuffer, java.nio.ByteBuffer, java.lang.StringBuilder, org.apache.logging.log4j.core.layout.ByteBufferDestination)' (org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder and org.apache.logging.log4j.core.layout.TextEncoderHelper are in unnamed module of loader 'app')
at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:165)
at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:134)
at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:125)
at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:89)
at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:542)
at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:500)
at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:483)
at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:417)
at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:82)
at org.apache.logging.log4j.core.Logger.log(Logger.java:161)
at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2205)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2159)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2142)
at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2017)
at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1983)
at org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1275)
at org.apache.ignite.logger.log4j2.Log4J2Logger.info(Log4J2Logger.java:472)
at org.apache.ignite.logger.log4j2.Log4J2Logger.info(Log4J2Logger.java:464)
at org.apache.ignite.internal.GridLoggerProxy.info(GridLoggerProxy.java:137)
at org.apache.ignite.internal.plugin.IgniteLogInfoProviderImpl.ackConfiguration(IgniteLogInfoProviderImpl.java:222)
at org.apache.ignite.internal.plugin.IgniteLogInfoProviderImpl.ackKernalInited(IgniteLogInfoProviderImpl.java:98)
at org.apache.ignite.internal.IgniteKernal.start(IgniteKernal.java:902)
at org.apache.ignite.internal.IgnitionEx$IgniteNamedInstance.start0(IgnitionEx.java:1799)
at org.apache.ignite.internal.IgnitionEx$IgniteNamedInstance.start(IgnitionEx.java:1721)
at org.apache.ignite.internal.IgnitionEx.start0(IgnitionEx.java:1160)
at org.apache.ignite.internal.IgnitionEx.startConfigurations(IgnitionEx.java:1054)
at org.apache.ignite.internal.IgnitionEx.start(IgnitionEx.java:940)
at org.apache.ignite.internal.IgnitionEx.start(IgnitionEx.java:839)
at org.apache.ignite.internal.IgnitionEx.start(IgnitionEx.java:709)
at org.apache.ignite.internal.IgnitionEx.start(IgnitionEx.java:678)
at org.apache.ignite.Ignition.start(Ignition.java:353)
at org.apache.ignite.startup.cmdline.CommandLineStartup.main(CommandLineStartup.java:365)
Caused by: java.lang.IllegalAccessError: class org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder tried to access method 'void org.apache.logging.log4j.core.layout.TextEncoderHelper.encodeText(java.nio.charset.CharsetEncoder, java.nio.CharBuffer, java.nio.ByteBuffer, java.lang.StringBuilder, org.apache.logging.log4j.core.layout.ByteBufferDestination)' (org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder and org.apache.logging.log4j.core.layout.TextEncoderHelper are in unnamed module of loader 'app')
at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder.encode(JsonTemplateLayout.java:241)
at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$StringBuilderEncoder.encode(JsonTemplateLayout.java:216)
at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.encode(JsonTemplateLayout.java:304)
at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.encode(JsonTemplateLayout.java:58)
at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197)
at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190)
at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181)
at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:161)
... 31 more
Solution
As Stanislav Lukyanov (see accepted answer) suggested the solution was to just download the JAR and place it below $IGNITE_HOME/libs. The error mentioned above was caused by a version mismatch. Having the following JARs with correct version made it work:
log4j-api-2.17.1.jar (default provided by ignite distribution)
log4j-core-2.17.1.jar (default provided by ignite distribution)
ignite-log4j2-2.13.0.jar (default provided by ignite distribution)
log4j-layout-template-json-2.17.1.jar (added, did not work with version 2.18.x)
If you run Ignite using Maven, you'll need to add the required dependency to your application POM, as described in the documentation:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-layout-template-json</artifactId>
<version>2.18.0</version>
</dependency>
If you run Ignite using a ZIP distribution, you'll need to download the dependency as a JAR, e.g. from here and add it to the $IGNITE_HOME/libs.

intellij : log4j does not output log file

I am trying to set up log4j in IntelliJ and having an issue. I am able to see console output, but no log file. My config is located at src/main/resources/Log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<!--<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> -->
<PatternLayout pattern="%d{HH:mm}%-5level %logger{36} - %msg%n" />
</Console>
<File name="FileAppender"
fileName="productivity.log"
immediateFlush="false"
append="true">
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="ConsoleAppender" />
<AppenderRef ref="FileAppender"/>
</Root>
</Loggers>
</Configuration>
I have done a lot of searching, most of the questions I found simply say to place the log4j2.xml file in src/main/resources, which I did. I also tried the suggestion here but that had no effect.
Also, this is a maven project...
I am currently just trying to run a test that prints one line of text, and no file is created. Any ideas?
Update
After some further testing and going thru the docs I was actually able to get log output, but only when the application context loadded.
When I run a unit test by itself, no log file is created, but I do get the console output.
The log4j2.xml still seems to be ignored, however. I my application.properties I entered
logging.file.path=log
logging.level.root=debug
logging.level.org.springframework.web=debug
and my logging output did come out in the log file, however, I defined my logger like so...
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// other code deleted for brevity...
private final static Logger logger = LogManager.getLogger(WidgetController.class);
I added this line in the method where the log message was called...
logger.info("in getAllWidgets");
logger.info("logger class : " + logger.getClass());
which created this output:
c.s.w.t.S.controller.WidgetController : in getAllWidgets
c.s.w.t.S.controller.WidgetController : logger class : class org.apache.logging.slf4j.SLF4JLogger
why is the class of the logger class org.apache.logging.slf4j.SLF4JLogger when it was clearly defined as a log4j logger?
if I look in my App/target/classes folder and see my log4j2.xml file there, does that mean it was successfully added to the classpath?
Also, this project was created by SpringInitializr.

Implementation of Qserver and Qmux

I'm learning about jpos. I got jpos programmer's guide with me and I have gone through that.
It helped me a lot in developing my switch.
I have some questions.
Whether I created my switch Correctly?
What are best practices for these situation:
I have a switch get diffrent messages (two types of channel/packager) from one switch and response back to it (qserver and qmux or only qmux or ...)
I have a switch get diffrent messages from diffrent banks and should reply them or forward messages to another (do I need qserver or just a qmux or need to have more configs on txmanager side)
now Im going to implement first situation In my switch flow is in this way:
Q2 will deploy by xml files:
Server.xml
RequestListener Class
TransactionMangare.xml
Here, in QMUX, what I don't understand is:
Q2 will deploy by xml files
(then I have QMUX.xml,Channeladaptor.xml and Listener Class [I want to know in what order it should come])
TransactionMangare.xml
messages with diffrent channel would come to me through a single port. is it possible?
as Qmux would use key (Bit41 and mti) which needs to specify channel before giving to the right one (otherwise would get 41 wrong or it would not parse it at all)
here are my deploy files:
logger.xml
<?xml version="1.0" encoding="UTF-8"?>
<logger name="Q2Logger" class="org.jpos.q2.qbean.LoggerAdaptor">
<log-listener class="org.jpos.util.SimpleLogListener" />
</logger>
server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server class="org.jpos.q2.iso.QServer" logger="Q2Logger" name="TransactionServer">
<attr name="port" type="java.lang.Integer">88800</attr>
<attr name="maxSessions" type="java.lang.Integer">20</attr>
<attr name="minSessions" type="java.lang.Integer">10</attr>
<!-- packager is customised (I also need another packager for another type of messages) -->
<channel class="org.jpos.iso.channel.ASCIIChannel" name="ASCIIChannel" logger="Q2Logger" packager="com.example.transaction.packager.ISO93APackager" header="ISO51300000">
<property name="timeout" value="70000" /> <!-- 7 minutes -->
<property name="keep-alive" value="true" />
</channel>
<!-- can have realm="incoming-request-listener" -> to automatically put into space ???? -->
<request-listener class="com.example.transaction.listener.ServerRequestListener" logger="Q2Logger" name="isoListener">
<property name="space" value="transient:default" />
<property name="queue" value="CerditCardTXNQueue" />
<property name="spaceTimeout" value="60000" />
</request-listener>
</server>
txnmgr.xml
<?xml version="1.0" encoding="UTF-8"?>
<txnmgr name="CreditCardTransactionManager" logger="Q2Logger" class="org.jpos.transaction.TransactionManager">
<property name="space" value="transient:default" />
<property name="queue" value="TXNQueue" />
<property name="sessions" value="5" />
<property name="debug" value="true" />
<participant class="com.example.transaction.participants.PrepareParticipant" logger="Q2Logger" />
<participant class="com.example.transaction.participants.ValidationParticipant" logger="Q2Logger" />
<participant class="com.example.transaction.participants.ProcessParticipant" logger="Q2Logger" />
<participant class="com.example.transaction.participants.SendResponseParticipant" logger="Q2Logger"/>
</txnmgr>
Thanks,

Must multiple Logback PropertyDefiner implementations be created to pull in multiple properties?

I am able to use a Logback PropertyDefiner to access a single property from a logback.xml configuration file. If I have 3 properties to access, I am currently using 3 separate implementations of PropertyDefiner (one for each property).
Is there a way to access multiple properties from a single PropertyDefiner implementation? Or perhaps there is another interface that supports multiple properties?
I want to be be able to use properties to plugin different values, based on environment (dev, ist, uat, perf, prod) for various logging configurations (context name, log levels, appender file names, file sizes, etc.).
I found this question, which is similar, but did not answer the question of how to access multiple properties.
Create a PropertyDefiner implementation class. The PropertyDefinerBase implementation is already provided.
package foo.bar;
import java.util.HashMap;
import java.util.Map;
import ch.qos.logback.core.PropertyDefinerBase;
public class LoggingPropertiesDefiner extends PropertyDefinerBase {
private static Map<String, String> properties = new HashMap<>();
static {
properties.put("encoderPattern", "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %message%n");
properties.put("maxFileSize", "50MB");
properties.put("rootLogLevel", "INFO");
}
private String propertyLookupKey;
public void setPropertyLookupKey(String propertyLookupKey) {
this.propertyLookupKey = propertyLookupKey;
}
#Override
public String getPropertyValue() {
//TODO In the real world, get properties from a properties loader.
return properties.get(propertyLookupKey);
}
}
In the logback.xml, use the same PropertyDefiner class for each property. Provide a different lookup key for each:
<define name="encoderPattern" class="foo.bar.LoggingPropertiesDefiner">
<propertyLookupKey>encoderPattern</propertyLookupKey>
</define>
<define name="maxFileSize" class="foo.bar.LoggingPropertiesDefiner">
<propertyLookupKey>maxFileSize</propertyLookupKey>
</define>
<define name="rootLogLevel" class="foo.bar.LoggingPropertiesDefiner">
<propertyLookupKey>rootLogLevel</propertyLookupKey>
</define>
Reference the property names in the logback.xml:
<root level="${rootLogLevel}">
<appender-ref ref="FILE"/>
</root>
You could use the property resource support from logback
logback.properties
mode=prod
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<property resource="logback.properties" />
<if condition='property("mode").equals("prod")'>
<then>
<include file="logback-prod.xml" />
</then>
</if>
<if condition='property("mode").equals("dev")'>
<then>
<include resource="logback-dev.xml" />
</then>
</if>
</configuration>
logback-prod.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--Daily rolling file appender -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${MYAPP_HOME}/myApp.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>myApp.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>2</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</Pattern>
</layout>
</appender>
<root level="ERROR">
<appender-ref ref="file" />
</root>
</configuration>
logback-dev.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--Daily rolling file appender -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${MYAPP_HOME}/myApp.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>myApp.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>2</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</Pattern>
</layout>
</appender>
<root level="TRACE">
<appender-ref ref="file" />
</root>
</configuration>
More detailed explaination here.
Does that help to resolve your problem?

WCF - One OperationContract sees Web.config, another doesn't

I have a WCF service with two methods, Ping and PutAddress. Ping works fine, but PutAddress was failing to initialize NHibernate correctly. Narrowing down the issue, I realized that the relevant settings from Web.config were not being read by PutAddress.
Strangely, Ping does have access to the settings from Web.config. I removed all of the NHibernate code and boiled it down to just trying to read the settings. Ping is able to read the settings (returns non-null, values in result are correct) while PutAddress' result is null.
Again, NHibernate is now completely out of the picture. Both methods simply try to read the relevant settings from Web.config. Ping succeeds while PutAddress fails.
Any thoughts?
Interface:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string Ping();
[OperationContract]
Address PutAddress(Address address);
}
Implementation:
public class MyService : IMyService
{
public string Ping()
{
NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration result =
(NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration)
System.Configuration.ConfigurationManager.GetSection("hibernate-configuration");
if (result == null)
{
System.Diagnostics.Debugger.Break(); // Does NOT break, "Pong" returned
}
return "Pong";
}
public Address PutAddress(Address address)
{
NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration result =
(NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration)
System.Configuration.ConfigurationManager.GetSection("hibernate-configuration");
if (result == null)
{
System.Diagnostics.Debugger.Break(); // Breaks, result is null
}
return address; // Return version potentially modified with DB-assigned ID
}
}
EDIT:
Here is the (sanitized) Web.config
<?xml version="1.0"?>
<configuration>
<configSections>
<!-- NHibernate Section -->
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
<!-- Log4Net Section -->
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
</configSections>
<!-- NHibernate Configuration -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">
NHibernate.Dialect.MySQLDialect
</property>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="connection.driver_class">
NHibernate.Driver.MySqlDataDriver
</property>
<property name="connection.connection_string">
Server=localhost;Database=DB;User ID=USER;Password=PASS
</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<property name="show_sql">true</property>
<mapping assembly="MyService"/>
</session-factory>
</hibernate-configuration>
<!-- Log4Net Configuration -->
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout, log4net">
<param name="ConversionPattern" value="%d %p %m%n"/>
</layout>
</appender>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender,log4net">
<param name="File" value="log.txt"/>
<param name="AppendToFile" value="true"/>
<param name="DatePattern" value="yyyy.MM.dd"/>
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%d %p %m%n"/>
</layout>
</appender>
<root>
<priority value="DEBUG"/>
<appender-ref ref="ConsoleAppender"/>
</root>
<logger name="NHibernate" additivity="false">
<level value="WARN"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="ConsoleAppender"/>
</logger>
<logger name="NHibernate.SQL" additivity="false">
<level value="ALL"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="ConsoleAppender"/>
</logger>
</log4net>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Found the problem - one call was from a Unit Test that I hand-coded, which uses MyServiceClient that was generated by Visual Studio when I added a Service Reference. The other call was from a unit test that was created by the Visual Studio (2010) Unit Test wizard, which just instantiated MyService as an object. The VS-generated unit test was running in-process as a result.
I had an odd problem myself. I could not return "DataTable" objects from a WCF operation. I wonder if there could be a problem with returning an "Address" object from a WCF operation as well.
I don't know the complete answer to your question, but I would suggest the following
In your code for PutAddress, try replacing Address PutAddress(Address address); with string PutAddress(Address address); and replace return address; with return "Pong"
By doing so, you will ascertain the flow of execution through your code. Make sure that you are actually breaking and returning null as opposed to returning a null value of address.
If the new code returns null, then you are in fact having trouble reading from the file. I suspect that it will actually return "Pong", indicating that your code is returning Address, which somehow contains null because of WCF weirdness.