システム間のマクロにおけるログの利用
12506 ワード
この記事では、システム間のアイリスでマクロに基づいたロギングシステムを設計し、構築します.いくつかinfo on macros .
ログ記録システム
ロギングシステムは、デバッグや監視中に多くの時間を節約するアプリケーションの作業を監視するための便利なツールです.私たちのシステムは2つの部分から成ります. ストレージクラス(ログレコード用) ログに新しいレコードを自動的に追加するマクロの集合 ストレージクラス
コンパイル中に、または実行時にこのデータが得られるときに指定するテーブルを作成しましょう.システムのマクロの2番目の部分で作業する際には、必要に応じてコンパイル時にログ処理可能な詳細を持つことを目指します.
インフォメーション
の間
イベントタイプ
編集
クラス名
編集
メソッド名
編集
メソッドに渡される引数
編集
CLSソースコードの行番号
ランタイム
生成されたintコードの行番号
ランタイム
ユーザー名
ランタイム
日付・時刻
ランタイム
メッセージ
ランタイム
IPアドレス
ランタイム
アプリを作成しましょう.上記の表からプロパティを含むログクラスです.ときにアプリ.ログオブジェクトは、ユーザー名、日付/時刻とIPアドレスのプロパティが自動的に記入され、作成されます.
アプリ.ログクラス:
通常、マクロは別の*に格納されます.incの定義を含むファイル.必要なファイルはinclude macrofilenameコマンドを使用してクラスに含めることができます.ログマクロ.
まず、ユーザーがアプリケーションのコードに追加するメインマクロを定義します.
私たちが今する必要があるのは、$ $ currentClass、$$$ CurrentMethod、$$$ Stackplace、$$$ method議論マクロを定義することです.最初の3つから始めましょう.
%引数のパッケージを使用してメソッド引数とその値のリストを取得しましょう.これは、メソッドの説明を含むクラスについてのすべての情報が含まれます.特に辞書に興味があります.CompiledMethodクラスとその形式のプロパティをリスト化します.
クラス名とメソッド名を取得する の対応するインスタンスをオープンする ソースコードラインに変換 アプリに対応するメソッドを追加しましょう.ログクラス:
次に、作成しましょう
改善
ログ記録システムを作成しました. まず最初に、オブジェクトの型引数を処理する可能性があります. 第二に、格納された引数値からメソッドのコンテキストを復元する呼び出し. オブジェクト型引数の処理
ログに引数値を設定する行は、ArgumentSlistToStringメソッドで生成され、次のようになります.
コンテキストの復元
このメソッドのアイデアは、すべての引数を現在のコンテキストで利用できるようにすることです.このためには、ProperdureBlockメソッドパラメーターを使用できます.0に設定すると、このメソッド内で宣言されたすべての変数は、メソッドを終了するときに使用可能なままになります.我々のメソッドは、アプリケーションのオブジェクトを開きます.log classと引数のプロパティを逆シリアル化します.
キーの潜在的な改善は、メソッドの内部で作成された変数の任意のリストを持つLogクラスに別の引数を追加することです.
結論
マクロは非常にアプリケーション開発に便利です.
質問
コンパイル中に行番号を取得する方法はありますか?
リンク Part I. Macros GitHub repository
ログ記録システム
ロギングシステムは、デバッグや監視中に多くの時間を節約するアプリケーションの作業を監視するための便利なツールです.私たちのシステムは2つの部分から成ります.
コンパイル中に、または実行時にこのデータが得られるときに指定するテーブルを作成しましょう.システムのマクロの2番目の部分で作業する際には、必要に応じてコンパイル時にログ処理可能な詳細を持つことを目指します.
インフォメーション
の間
イベントタイプ
編集
クラス名
編集
メソッド名
編集
メソッドに渡される引数
編集
CLSソースコードの行番号
ランタイム
生成されたintコードの行番号
ランタイム
ユーザー名
ランタイム
日付・時刻
ランタイム
メッセージ
ランタイム
IPアドレス
ランタイム
アプリを作成しましょう.上記の表からプロパティを含むログクラスです.ときにアプリ.ログオブジェクトは、ユーザー名、日付/時刻とIPアドレスのプロパティが自動的に記入され、作成されます.
アプリ.ログクラス:
Class App.Log Extends %Persistent
{
/// Type of event
Property EventType As %String(MAXLEN = 10, VALUELIST = ",NONE,FATAL,ERROR,WARN,INFO,STAT,DEBUG,RAW") [InitialExpression = "INFO"];
/// Name of class, where event happened
Property ClassName As %Dictionary.Classname(MAXLEN = 256);
/// Name of method, where event happened
Property MethodName As %String(MAXLEN = 128);
/// Line of int code
Property Source As %String(MAXLEN = 2000);
/// Line of cls code
Property SourceCLS As %String(MAXLEN = 2000);
/// IRIS user
Property UserName As %String(MAXLEN = 128) [InitialExpression = {$username}];
/// Arguments' values passed to method
Property Arguments As %String(MAXLEN = 32000, TRUNCATE = 1);
/// Date and time
Property TimeStamp As %TimeStamp [InitialExpression = {$zdt($h, 3, 1)}];
/// User message
Property Message As %String(MAXLEN = 32000, TRUNCATE = 1);
/// User IP address
Property ClientIPAddress As %String(MAXLEN = 32) [InitialExpression = {..GetClientAddress()}];
/// Determine user IP address
ClassMethod GetClientAddress()
{
// %CSP.Session source is preferable
#dim %request As %CSP.Request
If ($d(%request)) {
Return %request.CgiEnvs("REMOTE\_ADDR")
}
Return $system.Process.ClientIPAddress()
}
}
ログのマクロ通常、マクロは別の*に格納されます.incの定義を含むファイル.必要なファイルはinclude macrofilenameコマンドを使用してクラスに含めることができます.ログマクロ.
まず、ユーザーがアプリケーションのコードに追加するメインマクロを定義します.
#define LogEvent(%type, %message) Do ##class(App.Log).AddRecord($$$CurrentClass, $$$CurrentMethod, $$$StackPlace, %type, $$$MethodArguments, %message)
このマクロはイベントタイプとメッセージの2つの入力引数を受け付けます.message引数はユーザによって定義されますが、イベント型パラメーターには、イベントタイプを自動的に識別する別の名前のマクロが必要です.#define LogNone(%message) $$$LogEvent("NONE", %message)
#define LogError(%message) $$$LogEvent("ERROR", %message)
#define LogFatal(%message) $$$LogEvent("FATAL", %message)
#define LogWarn(%message) $$$LogEvent("WARN", %message)
#define LogInfo(%message) $$$LogEvent("INFO", %message)
#define LogStat(%message) $$$LogEvent("STAT", %message)
#define LogDebug(%message) $$$LogEvent("DEBUG", %message)
#define LogRaw(%message) $$$LogEvent("RAW", %message)
したがって、ログを実行するためには、ユーザーはアプリケーションコードで$$$ logerror(“追加メッセージ”)マクロを配置する必要があるだけです.私たちが今する必要があるのは、$ $ currentClass、$$$ CurrentMethod、$$$ Stackplace、$$$ method議論マクロを定義することです.最初の3つから始めましょう.
#define CurrentClass ##Expression($$$quote(%classname))
#define CurrentMethod ##Expression($$$quote(%methodname))
#define StackPlace $st($st(-1),"PLACE")
%classname
, %methodname
変数はdocumentation . The $stack 関数はintコード行番号を返す.CLS行番号に変換するにはcode .%引数のパッケージを使用してメソッド引数とその値のリストを取得しましょう.これは、メソッドの説明を含むクラスについてのすべての情報が含まれます.特に辞書に興味があります.CompiledMethodクラスとその形式のプロパティをリスト化します.
$lb($lb("Name","Classs","Type(Output/ByRef)","Default value "),...)
メソッドシグネチャに対応します.例えば、ClassMethod Test(a As %Integer = 1, ByRef b = 2, Output c)
の値は以下の形式になります:$lb(
$lb("a","%Library.Integer","","1"),
$lb("b","%Library.String","&","2"),
$lb("c","%Library.String","*","")
)
我々は作る必要がある$$$MethodArguments
マクロが次のコード(テストメソッド用)に展開します."a="_$g(a,"Null")_"; b="_$g(b,"Null")_"; c="_$g(c,"Null")_";"
これを実現するには、コンパイル中に次の手順を実行する必要があります.%Dictionary.CompiledMethod
クラスと取得FormalSpec
プロパティClassMethod GetMethodArguments(ClassName As %String, MethodName As %String) As %String
{
Set list = ..GetMethodArgumentsList(ClassName,MethodName)
Set string = ..ArgumentsListToString(list)
Return string
}
ClassMethod GetMethodArgumentsList(ClassName As %String, MethodName As %String) As %List
{
Set result = ""
Set def = ##class(%Dictionary.CompiledMethod).%OpenId(ClassName _ "||" _ MethodName)
If ($IsObject(def)) {
Set result = def.FormalSpecParsed
}
Return result
}
ClassMethod ArgumentsListToString(List As %List) As %String
{
Set result = ""
For i=1:1:$ll(List) {
Set result = result _ $$$quote($s(i>1=0:"",1:"; ") _ $lg($lg(List,i))_"=")
_ "_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_"
_$s(i=$ll(List)=0:"",1:$$$quote(";"))
}
Return result
}
さあ、定義しましょう$$$MethodArguments
マクロとして:#define MethodArguments ##Expression(##class(App.Log).GetMethodArguments(%classname,%methodname))
ユースケース次に、作成しましょう
App.Use
クラスATest
ログ記録システムの機能を実証する方法Include App.LogMacro
Class App.Use [ CompileAfter = App.Log ]
{
/// Do ##class(App.Use).Test()
ClassMethod Test(a As %Integer = 1, ByRef b = 2)
{
$$$LogWarn("Text")
}
}
その結果、$$$LogWarn("Text")
INTコードのマクロが次の行に変換されます.Do ##class(App.Log).AddRecord("App.Use","Test",$st($st(-1),"PLACE"),"WARN","a="\_$g(a,"Null")\_"; b="\_$g(b,"Null")\_";", "Text")
このコードの実行は、新しいアプリケーションを作成します.ログレコード改善
ログ記録システムを作成しました.
ログに引数値を設定する行は、ArgumentSlistToStringメソッドで生成され、次のようになります.
"\_$g(" \_ $lg($lg(List,i)) \_ ","\_$$$quote(..#Null)\_")\_"
リファクタリングを行い、変数名とクラスを受け入れる別のgetArgumentValueメソッドに移動しますFormalSpecParsed
) 変数を行に変換するコードを出力します.既存のデータ型を使用して、オブジェクトをJSONに変換しますSerializeObject
(ユーザコードからの呼び出しの場合)WriteJSONFromObject
(オブジェクトをJSONに変換する場合)ClassMethod GetArgumentValue(Name As %String, ClassName As %Dictionary.CacheClassname) As %String
{
If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
// it's an object
Return "_##class(App.Log).SerializeObject("_Name _ ")_"
} Else {
// it's a datatype
Return "_$g(" _ Name _ ","_$$$quote(..#Null)_")_"
}
}
ClassMethod SerializeObject(Object) As %String
{
Return:'$IsObject(Object) Object
Return ..WriteJSONFromObject(Object)
}
ClassMethod WriteJSONFromObject(Object) As %String [ ProcedureBlock = 0 ]
{
Set OldIORedirected = ##class(%Device).ReDirectIO()
Set OldMnemonic = ##class(%Device).GetMnemonicRoutine()
Set OldIO = $io
Try {
Set Str=""
//Redirect IO to the current routine - makes use of the labels defined below
Use $io::("^"_$ZNAME)
//Enable redirection
Do ##class(%Device).ReDirectIO(1)
Do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(Object)
} Catch Ex {
Set Str = ""
}
//Return to original redirection/mnemonic routine settings
If (OldMnemonic '= "") {
Use OldIO::("^"_OldMnemonic)
} Else {
Use OldIO
}
Do ##class(%Device).ReDirectIO(OldIORedirected)
Quit Str
// Labels that allow for IO redirection
// Read Character - we don't care about reading
rchr(c) Quit
// Read a string - we don't care about reading
rstr(sz,to) Quit
// Write a character - call the output label
wchr(s) Do output($char(s)) Quit
// Write a form feed - call the output label
wff() Do output($char(12)) Quit
// Write a newline - call the output label
wnl() Do output($char(13,10)) Quit
// Write a string - call the output label
wstr(s) Do output(s) Quit
// Write a tab - call the output label
wtab(s) Do output($char(9)) Quit
// Output label - this is where you would handle what you actually want to do.
// in our case, we want to write to Str
output(s) Set Str = Str_s Quit
}
オブジェクト型引数を持つログエントリは次のようになります.コンテキストの復元
このメソッドのアイデアは、すべての引数を現在のコンテキストで利用できるようにすることです.このためには、ProperdureBlockメソッドパラメーターを使用できます.0に設定すると、このメソッド内で宣言されたすべての変数は、メソッドを終了するときに使用可能なままになります.我々のメソッドは、アプリケーションのオブジェクトを開きます.log classと引数のプロパティを逆シリアル化します.
ClassMethod LoadContext(Id) As %Status [ ProcedureBlock = 0 ]
{
Return:'..%ExistsId(Id) $$$OK
Set Obj = ..%OpenId(Id)
Set Arguments = Obj.Arguments
Set List = ..GetMethodArgumentsList(Obj.ClassName,Obj.MethodName)
For i=1:1:$Length(Arguments,";")-1 {
Set Argument = $Piece(Arguments,";",i)
Set @$lg($lg(List,i)) = ..DeserializeObject($Piece(Argument,"=",2),$lg($lg(List,i),2))
}
Kill Obj,Arguments,Argument,i,Id,List
}
ClassMethod DeserializeObject(String, ClassName) As %String
{
If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
// it's an object
Set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(String,,.obj)
Return:$$$ISOK(st) obj
}
Return String
}
これが端末にどのように見えます.>zw
>do ##class(App.Log).LoadContext(2)
>zw
a=1
b=<OBJECT REFERENCE>[2@%ZEN.proxyObject]
>zw b
b=<OBJECT REFERENCE>[2@%ZEN.proxyObject]
+----------------- general information ---------------
| oref value: 2
| class name: %ZEN.proxyObject
| reference count: 2
+----------------- attribute values ------------------
| %changed = 1
| %data("prop1") = 123
| %data("prop2") = "abc"
| %index = ""
次は何ですか.キーの潜在的な改善は、メソッドの内部で作成された変数の任意のリストを持つLogクラスに別の引数を追加することです.
結論
マクロは非常にアプリケーション開発に便利です.
質問
コンパイル中に行番号を取得する方法はありますか?
リンク
Reference
この問題について(システム間のマクロにおけるログの利用), 我々は、より多くの情報をここで見つけました https://dev.to/intersystems/logging-using-macros-in-intersystems-iris-5dcnテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol