docs: private void handleStructLogExpression(final Stack<MethodAndParameter> stack,
                                             final MethodInvocationTree node,
                                             final Name name,
                                             final StatementInfo statementInfo,
                                             final ScannerParams scannerParams) {
          final java.util.List<VariableAndValue> usedVariables = new ArrayList<>();
          JCTree.JCLiteral literal = null;
          String level = null;
          String eventName = null;
  
          //statement check
          final StructLoggerFieldContext structLoggerFieldContext = scannerParams.getFields().get(name);
          final TypeMirror typeMirror = structLoggerFieldContext.getContextProvider();
          final VariableContextProvider variableContextProvider = scannerParams.getVarsHashMap().get(typeMirror);
  
          //go through each call of method in this method and check whether it can be mapped to logging variable provided by
          //VarContextProvider or it is logLevelMethod or log method call
          while (!stack.empty()) {
              // check whether method on stack matched variable from context provider, or is log or log level method
              boolean matched = false;
              final MethodAndParameter top = stack.pop();
              //go through each variable and check whether it matches
              for (Variable variable : variableContextProvider.getVariables()) {
                  final Name topMethodName = top.getMethodName();
  
                  if (variable.getName().equals(topMethodName)) {
                      addToUsedVariables(usedVariables, top, variable);
                      matched = true;
                      break;
                  }
                  else {
                      for (LogLevel logLevel : LogLevel.values()) {
                          if (topMethodName.contentEquals(logLevel.getLevelMethodName())) {
                              if (!(node.getArguments().get(0) instanceof JCTree.JCLiteral)) {
                                  printStatementMustHaveStringLiteralError(statementInfo, topMethodName);
                                  return;
                              }
                              literal = (JCTree.JCLiteral) node.getArguments().get(0);
                              level = logLevel.getLevelName();
                              matched = true;
                              break;
                          }
                          else if (topMethodName.contentEquals(logLevel.getLogEventMethodName())) {
                              return; // nothing to do here, no code replacement needed
                          }
                      }
                  }
              }
  
              if (top.getMethodName().contentEquals("log") && top.getParameter() != null) {
                  if (!(top.getParameter() instanceof JCTree.JCLiteral)) {
                      printStatementMustHaveStringLiteralError(statementInfo, top.getMethodName());
                      return;
                  }
                  eventName = ((JCTree.JCLiteral) top.getParameter()).getValue().toString();
                  if (!Pattern.compile("^(\\w+(\\.\\w+)*)+$").matcher(eventName).matches()) {
                      messager.printMessage(
                              Diagnostic.Kind.ERROR,
                              formatWithStatementLocation(
                                      "qualified event name %s specified by %s statement is not valid",
                                      statementInfo,
                                      eventName,
                                      statementInfo.getStatement()
                              )
                      );
                      return;
                  }
                  matched = true;
              } else if (top.getMethodName().contentEquals("log") && top.getParameter() == null) {
                  matched = true;
              }
              if (stack.empty() && !top.getMethodName().contentEquals("log")) {
                  messager.printMessage(
                          Diagnostic.Kind.ERROR,
                          formatWithStatementLocation(
                                  "statement %s must be ended by calling log() method",
                                  statementInfo,
                                  statementInfo.getStatement()
                          )
                  );
                  return;
              }
              if (!matched) {
                  messager.printMessage(
                          Diagnostic.Kind.ERROR,
                          formatWithStatementLocation(
                                  "variable %s in statement %s is not specified by variable context %s",
                                  statementInfo,
                                  top.getMethodName(),
                                  statementInfo.getStatement(),
                                  variableContextProvider.getTypeMirror()
                          )
                  );
                  return;
              }
          }
  
          //parametrization check
          if (variableContextProvider.shouldParametrize()) {
              final int countOfStringVariables = StringUtils.countMatches(literal.getValue().toString(), "{}");
              if (countOfStringVariables != usedVariables.size()) {
                  messager.printMessage(
                          Diagnostic.Kind.ERROR,
                          formatWithStatementLocation(
                                  "literal %s contains %d variables, but statement %s uses %d variables",
                                  statementInfo,
                                  literal.getValue().toString(),
                                  countOfStringVariables,
                                  statementInfo.getStatement(),
                                  usedVariables.size()
                          )
                  );
                  return;
              }
          }
  
          //event class generation
          JavaFile javaFile;
          try {
              javaFile = pojoService.createPojo(eventName, literal, usedVariables);
          } catch (PackageNameException e) {
              messager.printMessage(
                      Diagnostic.Kind.ERROR,
                      formatWithStatementLocation(
                              "qualified event name %s generated by statement %s is not valid, please check specified event name and package does not contain java keyword or no subpackage or class name starts with number",
                              statementInfo,
                              eventName,
                              statementInfo.getStatement()
                      )
              );
              return;
          }
          final String className = javaFile.typeSpec.name;
          final String qualifiedName = StringUtils.isBlank(javaFile.packageName) ? className : javaFile.packageName + "." + className;
          final GeneratedClassInfo generatedClassInfo = new GeneratedClassInfo(qualifiedName, className, (String) literal.getValue(), usedVariables, javaFile.packageName);
          for (GeneratedClassInfo info : scannerParams.getGeneratedClassesInfo()) {
              if (info.getQualifiedName().equals(generatedClassInfo.getQualifiedName())
                      && !info.getUsedVariables().equals(generatedClassInfo.getUsedVariables())
                      ) {
                  messager.printMessage(
                          Diagnostic.Kind.ERROR,
                          formatWithStatementLocation(
                                  "Statement %s generates different event structure for same event name",
                                  statementInfo,
                                  statementInfo.getStatement()
                          )
                  );
                  return;
              }
          }
          scannerParams.getGeneratedClassesInfo().add(generatedClassInfo);
  
          pojoService.writeJavaFile(javaFile);
  
          //replace statement
          replaceInCode(name.toString(), generatedClassInfo, statementInfo, usedVariables, literal, level, variableContextProvider);
      }