投稿者

インターシステムズジャパン
記事 Toshihiko Minamoto · 1月 11, 2021 4m read

データ変更の追跡 - 監査ログ - (2/2)

前回の記事では、データの変更を簡単に記録できる方法をお見せしました。 今回は、監査ログが記録されるデータ構造と監査データを記録する「Audit Abstract クラス」を変更しました。

また、データ構造は親構造と子構造に変更し、それぞれに「トランザクション」とそのトランザクションで「その値によって変更されたフィールド」を記録するテーブルが 2 つ設けられます。

新しいデータモデルをご覧ください。

「監査クラス」から変更したコードをご覧ください。

Class Sample.AuditBase [ Abstract ]
{
Trigger SaveAuditAfter [ CodeMode = objectgenerator, Event = INSERT/UPDATE, Foreach = row/object, Order = 99999, Time = AFTER ]
{
          #dim %compiledclass As %Dictionary.CompiledClass
          #dim tProperty As %Dictionary.CompiledProperty
          #dim tAudit As Sample.Audit
          Do %code.WriteLine($Char(9)"; get username and ip adress")
          Do %code.WriteLine($Char(9)"Set tSC = $$$OK")
          Do %code.WriteLine($Char(9)"Set tUsername = $USERNAME")
          Set tKey = ""
          Set tProperty = %compiledclass.Properties.GetNext(.tKey)
          Set tClassName = %compiledclass.Name
          Do %code.WriteLine($Char(9)"Try {")
          Do %code.WriteLine($Char(9,9)"; Check if the operation is an update - %oper = UPDATE")
          Do %code.WriteLine($Char(9,9)"if %oper = ""UPDATE"" { ")
          Do %code.WriteLine($Char(9,9,9)"Set tAudit = ##class(Sample.Audit).%New()")
          Do %code.WriteLine($Char(9,9,9)"Set tAudit.Date = +$Horolog")
          Do %code.WriteLine($Char(9,9,9)"Set tAudit.UserName = tUsername")
          Do %code.WriteLine($Char(9,9,9)"Set tAudit.ClassName = """tClassName"""")
          Do %code.WriteLine($Char(9,9,9)"Set tAudit.Id = {id}")
          Do %code.WriteLine($Char(9,9,9)"Set tSC = tAudit.%Save()")
          do %code.WriteLine($Char(9,9,9)"If $$$ISERR(tSC) $$$ThrowStatus(tSC)")
          Do %code.WriteLine($Char(9,9,9)"Set tAuditId = tAudit.%Id()")
          While tKey '= "" {
                    set tColumnNbr = $Get($$$EXTPROPsqlcolumnnumber($$$pEXT,%classname,tProperty.Name))
                    Set tColumnName = $Get($$$EXTPROPsqlcolumnname($$$pEXT,%classname,tProperty.Name))
                    If tColumnNbr '= "" {
                              Do %code.WriteLine($Char(9,9,9)";")
                              Do %code.WriteLine($Char(9,9,9)";")
                              Do %code.WriteLine($Char(9,9,9)"; Audit Field: "tProperty.SqlFieldName)
                              Do %code.WriteLine($Char(9,9,9)"if {"  tProperty.SqlFieldName _ "*C} {")
                              Do %code.WriteLine($Char(9,9,9,9)"Set tAuditField = ##class(Sample.AuditField).%New()")
                              Do %code.WriteLine($Char(9,9,9,9)"Set tAuditField.Field = """tColumnName"""")
                              Do %code.WriteLine($Char(9,9,9,9)"Set tAuditField.OldValue = {"tProperty.SqlFieldName"*O}")
                              Do %code.WriteLine($Char(9,9,9,9)"Set tAuditField.NewValue = {"tProperty.SqlFieldName"*N}")
                              Do %code.WriteLine($Char(9,9,9,9)"Do tAuditField.AuditSetObjectId(tAuditId)")
                              Do %code.WriteLine($Char(9,9,9,9)"Set tSC = tAuditField.%Save()")
                              do %code.WriteLine($Char(9,9,9,9)"If $$$ISERR(tSC) $$$ThrowStatus(tSC)")
                              Do %code.WriteLine($Char(9,9,9)"}")
                    }
                    Set tProperty = %compiledclass.Properties.GetNext(.tKey)
          }
          Do %code.WriteLine($Char(9,9)"}")
          Do %code.WriteLine($Char(9)"} Catch (tException) {")
                    Do %code.WriteLine($Char(9,9)"Set %msg = tException.AsStatus()")
                    Do %code.WriteLine($Char(9,9)"Set %ok = 0")
                    Do %code.WriteLine($Char(9)_"}")
                    Set %ok = 1
}
}

Test() クラスメソッドを使ってデータを変更することで、Audit クラス (Sample.Audit) から「parent record」が見えるようになり、「children fields」も「Audit Field」クラスから変更されていることが分かります。 (Sample.AuditField)  

d ##class(Sample.Person).Test(1)
INSERT INTO Sample.Person (Name, Age) VALUES ('TEST PARENT-CHILD', '01')
SQLCODE: 0
ID Age Name
1 01 TEST PARENT-CHILD
1 Rows(s) Affected
UPDATE Sample.Person SET Name = 'INTERSYSTEMS DEVELOPER COMMUNITY', Age = '100' WHERE Name = 'TEST PARENT-CHILD'
SQLCODE:0
ID Age Name
1 100 INTERSYSTEMS DEVELOPER COMMUNITY
1 Rows(s) Affected

監査クラス: 

Sample.AuditField の記録には、監査フィールド = 1 を使った Sample.Audit クラスへの参照があります。 以下に示すように、両クラスの関係を使えば、データに対してクエリを実行できます。

 これで完了です。 結果として、最初とは異なるログデータ構造ができました。