Один из вариантов решения приведен ниже. Поскольку стек вызова, как правило, выводится в info.add() после вызова методов Global::error(), warning(), info(), упоминание этих методов вырезается с помощью регулярных выражений из верхушки стека вызовов, чтобы не замусоривать его неинформативными строками. Метод можно вызывать как в X++, так и в CIL, правда, обработка параметра _skipLevels реализована только для X++
X++:
#define.PathPrefixLen (3)
#localmacro.ClientPrefix '(C)' #endmacro
#localmacro.ServerPrefix '(S)' #endmacro
#localmacro.RegexPatternFmt '^\\s*\\S+\\s+\%1\\.\%2.+\%1\\.(\%3\\.\%4|\%5\\.(\%6|\%7|\%8))\\(.+?\\n(.*)' #endmacro
#define.RegexResultStr ('$3')
#define.DotNetAppNamespace ('Dynamics.Ax.Application')
public static void callStack2infolog(str _prefix = "@SYS65092", Counter _skipLevels = 0)
{
System.Text.RegularExpressions.MatchCollection matches;
System.Text.RegularExpressions.Match oneMatch;
System.Diagnostics.StackTrace stackTrace;
container stack;
TreeNodePath correctPath;
TreeNodePath stackPath;
Counter n;
str regexPattern;
str clrStack;
int skipLevels = max(0, _skipLevels);
int line;
;
setPrefix(_prefix);
if (xSession::isCLRSession())
{
new InteropPermission(InteropKind::ClrInterop).assert();
stackTrace = new System.Diagnostics.StackTrace(true);
clrStack = stackTrace.ToString();
stackTrace = null;
regexPattern = strFmt( #RegexPatternFmt,
strReplace(#DotNetAppNamespace, '.', '\\.'),
strReplace(funcName(), '::', '\\.'),
classStr(Info),
methodStr(Info, Add),
classStr(Global),
staticMethodStr(Global, error),
staticMethodStr(Global, warning),
staticMethodStr(Global, info)
);
matches = System.Text.RegularExpressions.Regex::Matches(clrStack, regexPattern, System.Text.RegularExpressions.RegexOptions::Singleline);
n = matches.get_Count();
if (n > 0)
{
oneMatch = matches.get_Item(0);
clrStack = oneMatch.Result(#RegexResultStr);
oneMatch = null;
}
matches = null;
CodeAccessPermission::revertAssert();
info(clrStack);
}
else
{
stack = xSession::xppCallStack();
for (n = 3 + skipLevels * 2; n <= conLen(stack); n += 2)
{
stackPath = conPeek(stack,n);
line = conPeek(stack,n+1);
correctPath = stackPath;
if (line)
{
if ( substr(stackPath, 1, #PathPrefixLen) == #ClientPrefix
|| substr(stackPath, 1, #PathPrefixLen) == #ServerPrefix
)
{
correctPath = substr(stackPath, #PathPrefixLen + 1, maxint());
}
info(strFmt('%1 %2', stackPath, line),'', SysInfoAction_Editor::newLineColumn(correctPath, line));
}
else
{
info(strFmt('%1', stackPath));
}
}
}
}