log4net

在 .Net/mono 程序裡面使用 log4net,是極為簡單的事情.
首先到 log4net官方網站下載.
解開以後,到 bin 資料夾下,找適合你用的 assembly file. (p.s. 其實也不用特別找, .Net 與 mono 是通用的, .Net compact framework 與 sccli 我就沒試過了)
接著,視你自己專案的類型,該引用的就引用 (Visual Studio.Net/MonoDevelop/SharpDevelop),該改 Nant build file / Makefile 的就改.
步驟一:
使用 log4net 的第一步,是在你程序一開始的地方,去解析 log4net 的 configuration.

log4net.Config.XmlConfigurator.Configure();

一般性的 Windows form 程序通常是放在 Main 的一開始,而 asp.net 程序通常則是在 global.asax (codebehind 的話就是 global.asax.cs) 的 Application_Start 事件.
步驟二:
接著在你想要進行 log 的類別宣告這一個屬性.

protected static readonly ILog log = LogManager.GetLogger(typeof( your_class_name ));

要當成一般變數來用也行

ILog log = LogManager.GetLogger(typeof( your_class_name ));

步驟三:
最後是在你想要 log 的地方,放置這麼一行

log.Info( “your message here.” );

大功告成!!
就這麼簡單.
而 ILog 不只 Info() method 可以操作,還可以有 Debug(), Error(), Fatal(), Warn() 這幾個 method 可用,他們各代表了不同的層級.
層級的區分大致是這樣: Debug < Info < Warn < Error < Fatal
換句話說,當你 Configuration 裡的層級設定為 Debug 的時候,只要你有調用 ILog 的任何一個 method 來 log, 這些訊息都會出現在 log 裡面.
當你 Configuration 裡的層級設為 Warn 的時候,你有調用 Warn(), Error(), Fatal() 這三個 method 來 log 的地方,這些訊息都會出現在 log 裡面.
再來就是 Configuration.
Windows form 的 configuration 檔案通常名稱是 xxx.exe.config, asp.net 的 configuration 檔案則是 web.config
這裡主要放置兩個部份:

  • configSection:

    <configSections>
    <section name=”log4net” type=”log4net.Config.Log4NetConfigurationSectionHandler,log4net” />
    </configSections>

    p.s.這裡我有一個慘痛的教訓,查了好久,結果發現是打錯字, “log4net.Config” 我打成 “log4net.config”,一度我還懷疑是 configuration file 問題,還跑去 trace log4net 的程式 =_=”, 如果你確定你都設對了,該設的也都設定了,不妨利用 LogManager.GetRepository 取得 ILoggerRepository,去看 Configured 屬性,如果是 false,表示沒設定到,可以試著檢查大小寫看看.

  • log4net, 這邊請參考 log4net Config Examples 會比較快一點.

參考資料:

關於 ASP.NET 的 Cache(後續追蹤報導)

昨天在 Trace ASP.NET Caching 之後,還是念念不忘…
這麼好用的 OutputCacheModule 為甚麼會需要特別設定才會掛載??
於是今天再次 Trace, 這次從 Page 的產生開始.
於是我下手查了 Mono 的 PageCompiler 與 PageParser
他們之間的 Class diagram 大致是這樣
PageParser-20051216
在收到 Page Request 的時候,如果以前沒編譯過,Page Compiler 會先 Parse 這個 Page, 接著組出 code, 動態編譯成組件 (Assembly),然後再載入這個組件.
這樣一查下來,發現 PageParser 遇到 @ OutputCache 這個 Directive 的時候,也沒有動態去載入 OutputCacheModule 這個 Module.
這更怪了,再搜索 HttpModuleCollection.AddModule (.Net 是 HttpModuleCollection.LoadModule ),發現這真的只有在 HttpApplication 一開始的時候才有用到.
難道我昨天的結論真的沒有錯??
在萬念俱灰之下,利用 Google 搜索 OutputCacheModule
找到這篇文章:Extending the ASP.NET Runtime with Custom HTTP Modules
才發現,除了應用程式自訂的 web.config 之外,其實還有預設的 .config !!
以 .Net 1.1 來說,他是在 %WINDIR%\Microsoft.NET\Framework\v1.1.4322\CONFIG\machine.config
Mono 來說,他是在 /etc/mono/1.0/machine.config
打開來看之後,赫然發現在這裡, OutputCacheModule 已經被載入了!
所以並不需要特別在你自己的 web.config 去加載 OutputCacheModule!!
p.s. 這篇是意外的收穫,對於整個流程解析的不錯,僅供參考:ASP.NET 中 Session 實現原理淺析 (1) 會話的建立流程

關於 ASP.NET 的 Cache

一般人大概會想,這有甚麼好說的,不就是在頁面加上 @ OutputCache 指示詞(Directive), 這樣就表示會快取頁面.
列在 .Net framework SDK documentation 裡的詳細語法:

<%@ OutputCache Duration=”#ofseconds” Location=”Any | Client | Downstream | Server | None” Shared=”True | False” VaryByControl=”controlname” VaryByCustom=”browser | customstring” VaryByHeader=”headers” VaryByParam=”parametername” %>

或許吧,這沒啥大不了的,但我好奇的是,背地裡,.Net 是怎麼幫我完成這件事情的呢??
從 .Net Framework SDK documentation 裡面,幾乎無法找到相關資訊,解釋得很籠統.
於是我只能作這些猜想:
1. .Net 利用 HTTP/1.1 的 Caching 來實現.
2. 內部再使用 Cache hashtable 來避免對後端過於頻繁的存取.例如,取得資料以後,先放到 Cache,如果 Cache 裡面找的到(表示沒過期),就直接拿來顯示,否則的話就再去取資料.
我對第二點沒什麼意見,實際上應該是要這麼作.
但是第一點,我就有問題了. HTTP/1.1 的 Caching 機制對 server 端來說,server 只是送出特定的 HTTP header, 如 Cache-Control, Expires 等等. (參見 HTTP/1.1: Caching in HTTP ).
對 client 來說,他收到這些 header, 怎麼實做,反而是看他自己.如果是坊間抓網頁程式,他可以忽略這些 header, 卯起來抓,那麼這樣就沒有 cache 的效果啦~
另外一點,我感到懷疑的,在他語法裡,可以指定 Any/Client/Downstream/Server, 所謂的 Server, 又是指甚麼意義呢?
就我自己的認知,應該是 Server 在處理的時候,會把整個頁面放到 Cache 裡面.
當遇到有人索取這頁面的時候,就會檢查 Cache, 如果有就直接取出.
所以我查找 Mono 與 .Net 解釋 HttpApplication 的部份,他們都特別指出了 HttpApplication 收到 web request 以後處理的順序:

1. BeginRequest
2. AuthenticateRequest
3. AuthorizeRequest
4. ResolveRequestCache
5. AcquireRequestState
6.PreRequestHandlerExecute
7.PostRequestHandlerExecute
8.ReleaseRequestState
9.UpdateRequestCache
10.EndRequest

特別注意步驟 4 與步驟 9, 他們都提到了 Cache.
p.s. 我覺得 Mono Framework class reference 寫的比較清楚, 參閱 HttpApplication
接著,我又利用 Google 查找相關資訊.
發現有用mono應用程式的 web.config 多半都會加掛 OutputCacheModule 這個 Module.

<httpModules>
<add name=”OutputCache” type=”System.Web.Caching.OutputCacheModule”/>
</httpModules>

參考 mono 的源碼以後,發現這就是我要找的.
這個 Module 的確做了我推想的事情.
他在 OnResolveRequestCache 事件先依據 FilePath 去檢查 Cache, 如果存在,就直接以 Cache 內保存的內容輸出.
接著在 OnUpdateRequestCache 事件,檢查頁面是否需要被 Cache, 需要的話,就把輸出結果保存到 Cache 中.
Bingo!!
但這是mono的作法, .Net 是否也有同樣的類別呢??
我利用Reflector 這個工具去看 System.Web.dll, 的確, 有 OutputCacheModule 這個 Module.
至此,我們已經解決了我想要解決的問題,並做出一個小結論:
如果想對頁面作 Cache 的話,需要作兩件事情:
1. 在頁面的開始加上 @ OutputCache 指示詞.
2. 在 web.config 裡面加掛 OutputCacheModule
可是我接下來想知道的是,如果我用 .Net 去寫作的時候,也需要特別加掛這個 Module 嗎??
再次求助於 mono 的源碼, 從 HttpApplication.cs 開始查找.
我直接搜索 HttpModule, 因為我想知道 HttpApplication 是在何時載入 Module.
HttpApplication 正巧有一個 HttpModuleCollection, 這看來就是 HttpModule 的 Collection.
宣告的名稱為 modcoll, 接著以 modcoll 進行搜索.很快就找到 InitOnce, 這裡以 ModulesConfiguration 對 modcoll 進行了初始化.
接著再查看 ModulesConfiguration 與 HttpModulesConfigurationHandler 的源碼, 至此已經真相大白.
原來如果沒有在 web.config 指定要掛載的 module 的話,只會掛載內定的 DefaultAuthenticationModule.
那麼微軟的 .Net 呢??
Reflector 查找同樣的 Class, 並進行 disassembly.
果然也做了一樣的事情,沒指定要掛載的 module 的話,只會掛載內定的 DefaultAuthenticationModule.
補充結論:
除了小結論所提到的事項之外,一定要在 web.config 裡面指定要掛載 OutputCacheModule, 才會更能發揮 Cache 的功效.
參考資料:
*HTTP/1.1: Caching in HTTP
*Caching in ASP.NET
*Page Output Caching, Part 1
*ASP.NET Caching
*mono的源碼
*.Net framework SDK documentation

給定 assembly strong name 的方法

給定 assembly strong name 的方法
1.sn -k “your_snk.snk”
2.在你的 AssemblyInfo.cs 裡面加上
[assembly: AssemblyKeyFile(@”your_snk.snk”)]
3.編譯,收工.
Mono 也有同樣的指令,用法也完全一樣.
不再贅述.

混合編譯vb.net與c#

上次有人有在討論區問到怎麼混合編譯 vb.net 與 c# 的程式.
那時唯一的好解法,就是將 vb.net 程式編譯為 assembly
c# 程式也編譯為 assembly,這樣就能交互引用了.
這樣的缺點是,assembly 會比較多一些.
今天在CodeProject逛到這篇:Post build step static linking tool for C#, using ILMerge,恰好就可以解決此問題.
他引用了微軟研發ILMerge來作合併的工作.
不過很可惜的是,ILMerge目前還不能在Mono上使用.

其實是通用的…

本來以為, .Net 編譯出來的東西與 mono 是不通用的.
可是很奇怪,在網路上都找不到相關的文件來解釋為甚麼 nantmono 編譯會有警告/錯誤或是為甚麼 nhibernatemono 編譯會有警告/錯誤之類的問題.
照理來說,如果是很常見的問題,都應該早有人問了才對.
昨天,赫然發現,其實是通用的.
我用 .Net Framework 1.1 編譯好一個簡單的 hello world 程式,然後放到 linux 上,以 mono 執行.
果然可以執行…
換句話說……我是豬頭……
早就該想到了,如果都 follow ECMA 的標準,其實都是通用的才對!!!
真是笨.

mod_mono AutoConfiguration

Mono 1.1.10 前幾天 release 了,在 asp.net 方面,多了一個很方便的功能 – AutoConfiguration.
在之前,每次添加一個 asp.net application, 就需要去更動 mod_mono 設定,相當麻煩.
現在可以不用了,只要在 mod_mono.conf 添加

MonoAutoApplication enabled

之後,就可以很方便的跟寫 php application 一樣,建立目錄以後,就等同於是建立 application.
舉例來說,你在你的 Home directory 編修一個 asp.net 網頁

$ echo $USER
rupert
$ cd
$ cd public_html
$ mkdir demo
$ cd demo
$ edit hello.aspx

那麼,你編寫完以後,就可以直接

http://your_server/~rupert/demo/hello.aspx

真的是…太方便啦… 🙂

Mono Directions

今早在LinuxToday上看到Mono Directions
裡面報導了目前的現況,以及未來的方向.
我只摘錄我有興趣的幾個項目:
*mono 1.2 要等到 Windows.Form 完備以後才 release
*gmcs (.Net 2.0 c# compiler) 除了 nullable types 之外,已經齊全.
*monodevelop, 可以寫 plugin 來 enhance 了,我最期待的是 asp.net designer…
*asp.net 2.0 在進行中,但已經有不少 control 可用,而 Atlas 也開始實做.
*msbuild 也有替代品 – xbuild…但還沒作為正式 release 的內容 (Google Summer of code projects 的成品)
*xaml 也有人實作了. (Google Summer of code projects 的成品)
*ADO.Net 2.0 進行中.
其他又學到的,原來有 mono debugger可用…
介面非常類似 gdb,對熟悉 linux 程式寫作的人,不難上手.

.Net attribute

attribute, 用來為 class, method, parameters 貼上標籤,以便後續的應用.
所有的 attribute class 都要繼承 System.Attribute

class Class1Attribute: System.Attribute
{
}

使用的時候可以省略 Attribute

[Class1]
class Class2
{
}

打全名當然也是可以

[Class1Attribute]
class Class2
{
}

使用 xxx( yyy ) 時,attribute 的 constructor 會被套用,而 attribute class 裡的 property 會被自動認定為 named parameter.

class Class1Attribute: System.Attribute
{
private int _id;
private string _name;
public Class1Attribute( int id )
{
_id = id;
}
public string Name
{
get { return _name };
set { _name = value };
}
}
[Class1( 1, Name="This is a test" )]
class class2
{
}

也可以貼很多標籤上去

[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
class Class1Attribute: System.Attribute
{
....
}
[Class1( 1, Name="This is a test" ), Class1( 2, Name="Test2" )]
class class2
{
}

但是要怎麼應用呢? 從 .Net framework documentation 裡的 sample, 大致上可以猜到.

using System;
using System.Reflection;
namespace CustomAttrCS {
// An enumeration of animals. Start at 1 (0 = uninitialized).
public enum Animal {
// Pets.
Dog = 1,
Cat,
Bird,
}
// A custom attribute to allow a target to have a pet.
public class AnimalTypeAttribute : Attribute {
// The constructor is called when the attribute is set.
public AnimalTypeAttribute(Animal pet) {
thePet = pet;
}
// Keep a variable internally ...
protected Animal thePet;
// .. and show a copy to the outside world.
public Animal Pet {
get { return thePet; }
set { thePet = Pet; }
}
}
// A test class where each method has its own pet.
class AnimalTypeTestClass {
[AnimalType(Animal.Dog)]
public void DogMethod() {}
[AnimalType(Animal.Cat)]
public void CatMethod() {}
[AnimalType(Animal.Bird)]
public void BirdMethod() {}
}
class DemoClass {
static void Main(string[] args) {
AnimalTypeTestClass testClass = new AnimalTypeTestClass();
Type type = testClass.GetType();
// Iterate through all the methods of the class.
foreach(MethodInfo mInfo in type.GetMethods()) {
// Iterate through all the Attributes for each method.
foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo)) {
// Check for the AnimalType attribute.
if (attr.GetType() == typeof(AnimalTypeAttribute))
Console.WriteLine(
"Method {0} has a pet {1} attribute.",
mInfo.Name, ((AnimalTypeAttribute)attr).Pet);
}
}
}
}
}

所以你可以看到,在貼上標籤以後,其實對 Class 本身來說沒啥作用,會認為有用的,是其他的 Class, 他們可以依據這些 Attribute 來作一些特定的處理.
這邊有一系列的文章,還提出了應用.

系列 2~4 介紹一個 sql generator 的範例.
系列 5~6 是一個比較深的範例,牽涉到 Remoting 的東西
.Net framework 內也預定了不少 Attribute,我覺得還蠻有用的.
例如: Conditional attribute
以下面的範例來說,如果 DEBUG 沒被定義,那麼 Class1.M() 將不會被執行.
這倒是一個不錯的debug方法

#define DEBUG
using System;
using System.Diagnostics;
class Class1
{
[Conditional("DEBUG")]
public static void M() {
Console.WriteLine("Executed Class1.M");
}
}
class Class2
{
public static void Test() {
Class1.M();
}
}

仔細想想也可以用來作版本控管喔…某版本可以用這個 method, 但是某版本不行….
還有就是 Obsolete, 可以用來指示 class 或 method 即將被淘汰