类org.apache.log4j.helpers.OptionConverter源码实例Demo

下面列出了怎么用org.apache.log4j.helpers.OptionConverter的API类实例代码及写法,或者点击链接到github查看源代码。

源代码1 项目: sofa-common-tools   文件: Log4jTest.java
@Test
public void testIndependentSpaceLog4j() {
    LoggerRepository repo1 = new Hierarchy(new RootLogger((Level) Level.DEBUG));
    URL url1 = LogbackTest.class.getResource("/com/alipay/sofa/rpc/log/log4j/log-conf.xml");
    OptionConverter.selectAndConfigure(url1, null, repo1);
    Logger logger1 = repo1.getLogger("com.foo.Bar");
    Assert.assertNotNull(logger1);

    //log4j logger 2

    LoggerRepository repo2 = new Hierarchy(new RootLogger((Level) Level.DEBUG));
    URL url2 = LogbackTest.class.getResource("/com/alipay/sofa/rpc/log/log4j/log4j_b.xml");
    OptionConverter.selectAndConfigure(url2, null, repo2);
    Logger logger2 = repo1.getLogger("com.foo.Bar2");
    Assert.assertNotNull(logger2);

    Assert.assertNotSame(logger1, logger2);
}
 
源代码2 项目: cacheonix-core   文件: PropertyConfigurator.java
void configureRootCategory(Properties props, LoggerRepository hierarchy) {
   String effectiveFrefix = ROOT_LOGGER_PREFIX;
   String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);

   if(value == null) {
     value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
     effectiveFrefix = ROOT_CATEGORY_PREFIX;
   }

   if(value == null)
     LogLog.debug("Could not find root logger information. Is this OK?");
   else {
     Logger root = hierarchy.getRootLogger();
     synchronized(root) {
parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
     }
   }
 }
 
源代码3 项目: cacheonix-core   文件: AppenderDynamicMBean.java
public
Object invoke(String operationName, Object params[], String signature[])
  throws MBeanException,
  ReflectionException {

  if(operationName.equals("activateOptions") &&
                   appender instanceof OptionHandler) {
    OptionHandler oh = (OptionHandler) appender;
    oh.activateOptions();
    return "Options activated.";
  } else if (operationName.equals("setLayout")) {
    Layout layout = (Layout) OptionConverter.instantiateByClassName((String)
						      params[0],
						      Layout.class,
						      null);
    appender.setLayout(layout);
    registerLayoutMBean(layout);
  }
  return null;
}
 
源代码4 项目: cacheonix-core   文件: RendererMap.java
/**
    Add a renderer to a hierarchy passed as parameter.
 */
 static
 public
 void addRenderer(RendererSupport repository, String renderedClassName,
	   String renderingClassName) {
   LogLog.debug("Rendering class: ["+renderingClassName+"], Rendered class: ["+
	 renderedClassName+"].");
   ObjectRenderer renderer = (ObjectRenderer)
            OptionConverter.instantiateByClassName(renderingClassName,
					    ObjectRenderer.class,
					    null);
   if(renderer == null) {
     LogLog.error("Could not instantiate renderer ["+renderingClassName+"].");
     return;
   } else {
     try {
Class renderedClass = Loader.loadClass(renderedClassName);
repository.setRenderer(renderedClass, renderer);
     } catch(ClassNotFoundException e) {
LogLog.error("Could not find class ["+renderedClassName+"].", e);
     }
   }
 }
 
源代码5 项目: cacheonix-core   文件: PropertySetter.java
/**
   Convert <code>val</code> a String parameter to an object of a
   given type.
*/
protected
Object convertArg(String val, Class type) {
  if(val == null)
    return null;

  String v = val.trim();
  if (String.class.isAssignableFrom(type)) {
    return val;
  } else if (Integer.TYPE.isAssignableFrom(type)) {
    return new Integer(v);
  } else if (Long.TYPE.isAssignableFrom(type)) {
    return new Long(v);
  } else if (Boolean.TYPE.isAssignableFrom(type)) {
    if ("true".equalsIgnoreCase(v)) {
      return Boolean.TRUE;
    } else if ("false".equalsIgnoreCase(v)) {
      return Boolean.FALSE;
    }
  } else if (Priority.class.isAssignableFrom(type)) {
    return OptionConverter.toLevel(v, (Level) Level.DEBUG);
  }
  return null;
}
 
源代码6 项目: logging-log4j2   文件: XmlConfiguration.java
/**
 * Used internally to parse an {@link ErrorHandler} element.
 */
private void parseErrorHandler(Element element, Appender appender) {
    ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
            subst(element.getAttribute(CLASS_ATTR)),
            ErrorHandler.class,
            null);

    if (eh != null) {
        eh.setAppender(appender);

        PropertySetter propSetter = new PropertySetter(eh);
        forEachElement(element.getChildNodes(), (currentElement) -> {
            String tagName = currentElement.getTagName();
            if (tagName.equals(PARAM_TAG)) {
                setParameter(currentElement, propSetter);
            }
        });
        propSetter.activate();
        appender.setErrorHandler(eh);
    }
}
 
源代码7 项目: logging-log4j2   文件: PropertiesConfiguration.java
private void configureRoot(Properties props) {
    String effectiveFrefix = ROOT_LOGGER_PREFIX;
    String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);

    if (value == null) {
        value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
        effectiveFrefix = ROOT_CATEGORY_PREFIX;
    }

    if (value == null) {
        LOGGER.debug("Could not find root logger information. Is this OK?");
    } else {
        LoggerConfig root = getRootLogger();
        parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
    }
}
 
源代码8 项目: logging-log4j2   文件: PropertiesConfiguration.java
public Appender parseAppender(Properties props, String appenderName) {
    Appender appender = registry.get(appenderName);
    if ((appender != null)) {
        LOGGER.debug("Appender \"" + appenderName + "\" was already parsed.");
        return appender;
    }
    // Appender was not previously initialized.
    final String prefix = APPENDER_PREFIX + appenderName;
    final String layoutPrefix = prefix + ".layout";
    final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
    String className = OptionConverter.findAndSubst(prefix, props);
    appender = manager.parseAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props, this);
    if (appender == null) {
        appender = buildAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props);
    } else {
        registry.put(appenderName, appender);
        if (appender instanceof AppenderWrapper) {
            addAppender(((AppenderWrapper) appender).getAppender());
        } else {
            addAppender(new AppenderAdapter(appender).getAdapter());
        }
    }
    return appender;
}
 
源代码9 项目: logging-log4j2   文件: AsyncAppenderBuilder.java
private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
        String[] appenderRefs, boolean blocking, int bufferSize, boolean includeLocation,
        T configuration) {
    org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
            org.apache.logging.log4j.Level.TRACE);
    AppenderRef[] refs = new AppenderRef[appenderRefs.length];
    int index = 0;
    for (String appenderRef : appenderRefs) {
        refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
    }
    return new AppenderWrapper(AsyncAppender.newBuilder()
            .setName(name)
            .setAppenderRefs(refs)
            .setBlocking(blocking)
            .setBufferSize(bufferSize)
            .setIncludeLocation(includeLocation)
            .setConfiguration(configuration)
            .build());
}
 
源代码10 项目: logging-log4j2   文件: RewriteAppenderBuilder.java
private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
        String[] appenderRefs, RewritePolicy policy, Filter filter, T configuration) {
    org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
            org.apache.logging.log4j.Level.TRACE);
    AppenderRef[] refs = new AppenderRef[appenderRefs.length];
    int index = 0;
    for (String appenderRef : appenderRefs) {
        refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
    }
    org.apache.logging.log4j.core.Filter rewriteFilter = buildFilters(level, filter);
    org.apache.logging.log4j.core.appender.rewrite.RewritePolicy rewritePolicy;
    if (policy instanceof RewritePolicyWrapper) {
        rewritePolicy = ((RewritePolicyWrapper) policy).getPolicy();
    } else {
        rewritePolicy = new RewritePolicyAdapter(policy);
    }
    return new AppenderWrapper(RewriteAppender.createAppender(name, true, refs, configuration,
            rewritePolicy, rewriteFilter));
}
 
源代码11 项目: logging-log4j2   文件: XmlConfigurationFactory.java
/**
 * Used internally to parse an {@link ErrorHandler} element.
 */
private void parseErrorHandler(Element element, Appender appender) {
    ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
            subst(element.getAttribute(CLASS_ATTR)),
            ErrorHandler.class,
            null);

    if (eh != null) {
        eh.setAppender(appender);

        PropertySetter propSetter = new PropertySetter(eh);
        forEachElement(element.getChildNodes(), (currentElement) -> {
            String tagName = currentElement.getTagName();
            if (tagName.equals(PARAM_TAG)) {
                setParameter(currentElement, propSetter);
            }
        });
        propSetter.activate();
        appender.setErrorHandler(eh);
    }
}
 
源代码12 项目: cacheonix-core   文件: PropertyConfigurator.java
/**
    Read configuration options from <code>properties</code>.

    See {@link #doConfigure(String, LoggerRepository)} for the expected format.
 */
 public
 void doConfigure(Properties properties, LoggerRepository hierarchy) {
   String value = properties.getProperty(LogLog.DEBUG_KEY);
   if(value == null) {
     value = properties.getProperty("log4j.configDebug");
     if(value != null)
LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
   }

   if(value != null) {
     LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
   }

     //
     //   if log4j.reset=true then
     //        reset hierarchy
   String reset = properties.getProperty(RESET_KEY);
   if (reset != null && OptionConverter.toBoolean(reset, false)) {
         hierarchy.resetConfiguration();
   }

   String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
					       properties);
   if(thresholdStr != null) {
     hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
					     (Level) Level.ALL));
     LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
   }

   configureRootCategory(properties, hierarchy);
   configureLoggerFactory(properties);
   parseCatsAndRenderers(properties, hierarchy);

   LogLog.debug("Finished configuring.");
   // We don't want to hold references to appenders preventing their
   // garbage collection.
   registry.clear();
 }
 
源代码13 项目: cacheonix-core   文件: PropertyConfigurator.java
/**
   Check the provided <code>Properties</code> object for a
   {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}
   entry specified by {@link #LOGGER_FACTORY_KEY}.  If such an entry
   exists, an attempt is made to create an instance using the default
   constructor.  This instance is used for subsequent Category creations
   within this configurator.

   @see #parseCatsAndRenderers
 */
protected void configureLoggerFactory(Properties props) {
  String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,
					   props);
  if(factoryClassName != null) {
    LogLog.debug("Setting category factory to ["+factoryClassName+"].");
    loggerFactory = (LoggerFactory)
         OptionConverter.instantiateByClassName(factoryClassName,
					 LoggerFactory.class,
					 loggerFactory);
    PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");
  }
}
 
源代码14 项目: cacheonix-core   文件: PropertyConfigurator.java
/**
    Parse non-root elements, such non-root categories and renderers.
 */
 protected
 void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
   Enumeration enumeration = props.propertyNames();
   while(enumeration.hasMoreElements()) {
     String key = (String) enumeration.nextElement();
     if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
String loggerName = null;
if(key.startsWith(CATEGORY_PREFIX)) {
  loggerName = key.substring(CATEGORY_PREFIX.length());
} else if(key.startsWith(LOGGER_PREFIX)) {
  loggerName = key.substring(LOGGER_PREFIX.length());
}
String value =  OptionConverter.findAndSubst(key, props);
Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
synchronized(logger) {
  parseCategory(props, logger, key, loggerName, value);
  parseAdditivityForLogger(props, logger, loggerName);
}
     } else if(key.startsWith(RENDERER_PREFIX)) {
String renderedClass = key.substring(RENDERER_PREFIX.length());
String renderingClass = OptionConverter.findAndSubst(key, props);
if(hierarchy instanceof RendererSupport) {
  RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
			  renderingClass);
}
     }
   }
 }
 
源代码15 项目: cacheonix-core   文件: PropertyConfigurator.java
/**
   Parse the additivity option for a non-root category.
 */
void parseAdditivityForLogger(Properties props, Logger cat,
		  String loggerName) {
  String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName,
			     props);
  LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]");
  // touch additivity only if necessary
  if((value != null) && (!value.equals(""))) {
    boolean additivity = OptionConverter.toBoolean(value, true);
    LogLog.debug("Setting additivity for \""+loggerName+"\" to "+
   additivity);
    cat.setAdditivity(additivity);
  }
}
 
源代码16 项目: cacheonix-core   文件: StringMatchFilter.java
/**
   @deprecated Use the setter method for the option directly instead
   of the generic <code>setOption</code> method. 
*/
public
void setOption(String key, String value) { 
  
  if(key.equalsIgnoreCase(STRING_TO_MATCH_OPTION)) {
    stringToMatch = value;
  } else if (key.equalsIgnoreCase(ACCEPT_ON_MATCH_OPTION)) {
    acceptOnMatch = OptionConverter.toBoolean(value, acceptOnMatch);
  }
}
 
源代码17 项目: cacheonix-core   文件: HierarchyDynamicMBean.java
public
void setAttribute(Attribute attribute) throws AttributeNotFoundException,
                                              InvalidAttributeValueException,
                                              MBeanException,
                                              ReflectionException {

  // Check attribute is not null to avoid NullPointerException later on
  if (attribute == null) {
    throw new RuntimeOperationsException(
                new IllegalArgumentException("Attribute cannot be null"),
 "Cannot invoke a setter of "+dClassName+" with null attribute");
  }
  String name = attribute.getName();
  Object value = attribute.getValue();

  if (name == null) {
    throw new RuntimeOperationsException(
             new IllegalArgumentException("Attribute name cannot be null"),
      "Cannot invoke the setter of "+dClassName+
      " with null attribute name");
  }

  if(name.equals(THRESHOLD)) {
    Level l = OptionConverter.toLevel((String) value,
			   hierarchy.getThreshold());
    hierarchy.setThreshold(l);
  }


}
 
源代码18 项目: cacheonix-core   文件: LoggerDynamicMBean.java
void addAppender(String appenderClass, String appenderName) {
  cat.debug("addAppender called with "+appenderClass+", "+appenderName);
  Appender appender = (Appender)
     OptionConverter.instantiateByClassName(appenderClass,
			      org.apache.log4j.Appender.class,
			      null);
  appender.setName(appenderName);
  logger.addAppender(appender);

  //appenderMBeanRegistration();

}
 
源代码19 项目: cacheonix-core   文件: DOMConfigurator.java
/**
    Used internally to parse a filter element.
  */
 protected
 void parseFilters(Element element, Appender appender) {
   String clazz = subst(element.getAttribute(CLASS_ATTR));
   Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
                                               Filter.class, null);
   
   if(filter != null) {
     PropertySetter propSetter = new PropertySetter(filter);
     NodeList children = element.getChildNodes();
     final int length 	= children.getLength();

     for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
  Element currentElement = (Element) currentNode;
  String tagName = currentElement.getTagName();
  if(tagName.equals(PARAM_TAG)) {
           setParameter(currentElement, propSetter);
  } else {
           quietParseUnrecognizedElement(filter, currentElement, props);
     }
}
     }
     propSetter.activate();
     LogLog.debug("Adding filter of type ["+filter.getClass()
	   +"] to appender named ["+appender.getName()+"].");
     appender.addFilter(filter);
   }    
 }
 
源代码20 项目: cacheonix-core   文件: DOMConfigurator.java
/**
    Used internally to parse a level  element.
 */
 protected
 void parseLevel(Element element, Logger logger, boolean isRoot) {
   String catName = logger.getName();
   if(isRoot) {
     catName = "root";
   }

   String priStr = subst(element.getAttribute(VALUE_ATTR));
   LogLog.debug("Level value for "+catName+" is  ["+priStr+"].");
   
   if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
     if(isRoot) {
LogLog.error("Root level cannot be inherited. Ignoring directive.");
     } else {
logger.setLevel(null);
     }
   } else {
     String className = subst(element.getAttribute(CLASS_ATTR));      
     if(EMPTY_STR.equals(className)) {	
logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
     } else {
LogLog.debug("Desired Level sub-class: ["+className+']');
try {	 
  Class clazz = Loader.loadClass(className);
  Method toLevelMethod = clazz.getMethod("toLevel", 
					    ONE_STRING_PARAM);
  Level pri = (Level) toLevelMethod.invoke(null, 
					    new Object[] {priStr});
  logger.setLevel(pri);
} catch (Exception oops) {
  LogLog.error("Could not create level ["+priStr+
	       "]. Reported error follows.", oops);
  return;
}
     }
   }
   LogLog.debug(catName + " level set to " + logger.getLevel());    
 }
 
源代码21 项目: cacheonix-core   文件: DOMConfigurator.java
/**
 * Sets a parameter based from configuration file content.
 *
 * @param elem       param element, may not be null.
 * @param propSetter property setter, may not be null.
 * @param props      properties
 * @since 1.2.15
 */
public static void setParameter(final Element elem,
                                final PropertySetter propSetter,
                                final Properties props) {
    String name = subst(elem.getAttribute("name"), props);
    String value = (elem.getAttribute("value"));
    value = subst(OptionConverter.convertSpecialChars(value), props);
    propSetter.setProperty(name, value);
}
 
源代码22 项目: cacheonix-core   文件: DOMConfigurator.java
/**
 * Creates an object and processes any nested param elements
 * but does not call activateOptions.  If the class also supports
 * UnrecognizedElementParser, the parseUnrecognizedElement method
 * will be call for any child elements other than param.
 *
 * @param element       element, may not be null.
 * @param props         properties
 * @param expectedClass interface or class expected to be implemented
 *                      by created class
 * @return created class or null.
 * @throws Exception thrown if the contain object should be abandoned.
 * @since 1.2.15
 */
public static Object parseElement(final Element element,
                                         final Properties props,
                                         final Class expectedClass) throws Exception {
    String clazz = subst(element.getAttribute("class"), props);
    Object instance = OptionConverter.instantiateByClassName(clazz,
            expectedClass, null);

    if (instance != null) {
        PropertySetter propSetter = new PropertySetter(instance);
        NodeList children = element.getChildNodes();
        final int length = children.getLength();

        for (int loop = 0; loop < length; loop++) {
            Node currentNode = children.item(loop);
            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                Element currentElement = (Element) currentNode;
                String tagName = currentElement.getTagName();
                if (tagName.equals("param")) {
                    setParameter(currentElement, propSetter, props);
                } else {
                     parseUnrecognizedElement(instance, currentElement, props);
                }
            }
        }
        return instance;
    }
    return null;
}
 
源代码23 项目: cacheonix-core   文件: PropertySetter.java
/**
    Set the properites for the object that match the
    <code>prefix</code> passed as parameter.

    
  */
 public
 void setProperties(Properties properties, String prefix) {
   int len = prefix.length();
   
   for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
     String key = (String) e.nextElement();
     
     // handle only properties that start with the desired frefix.
     if (key.startsWith(prefix)) {


// ignore key if it contains dots after the prefix
       if (key.indexOf('.', len + 1) > 0) {
  //System.err.println("----------Ignoring---["+key
  //	     +"], prefix=["+prefix+"].");
  continue;
}
       
String value = OptionConverter.findAndSubst(key, properties);
       key = key.substring(len);
       if ("layout".equals(key) && obj instanceof Appender) {
         continue;
       }        
       setProperty(key, value);
     }
   }
   activate();
 }
 
源代码24 项目: cacheonix-core   文件: SMTPAppender.java
/**
   The <b>EvaluatorClass</b> option takes a string value
   representing the name of the class implementing the {@link
   TriggeringEventEvaluator} interface. A corresponding object will
   be instantiated and assigned as the triggering event evaluator
   for the SMTPAppender.
 */
public
void setEvaluatorClass(String value) {
    evaluator = (TriggeringEventEvaluator)
              OptionConverter.instantiateByClassName(value,
			   TriggeringEventEvaluator.class,
				       evaluator);
}
 
源代码25 项目: cacheonix-core   文件: OptionConverterTestCase.java
public
void varSubstTest1() {
  String r;

  r = OptionConverter.substVars("hello world.", null);
  assertEquals("hello world.", r);
  
  r = OptionConverter.substVars("hello ${TOTO} world.", null);
  
  assertEquals("hello wonderful world.", r);
}
 
源代码26 项目: cacheonix-core   文件: OptionConverterTestCase.java
public
void varSubstTest2() {
  String r;

  r = OptionConverter.substVars("Test2 ${key1} mid ${key2} end.", null);
  assertEquals("Test2 value1 mid value2 end.", r);
}
 
源代码27 项目: cacheonix-core   文件: OptionConverterTestCase.java
public
void varSubstTest3() {
  String r;

  r = OptionConverter.substVars(
		     "Test3 ${unset} mid ${key1} end.", null);
  assertEquals("Test3  mid value1 end.", r);
}
 
源代码28 项目: cacheonix-core   文件: OptionConverterTestCase.java
public
void varSubstTest4() {
  String res;
  String val = "Test4 ${incomplete ";
  try {
    res = OptionConverter.substVars(val, null);
  }
  catch(IllegalArgumentException e) {
    String errorMsg = e.getMessage();
    //System.out.println('['+errorMsg+']');
    assertEquals('"'+val
   + "\" has no closing brace. Opening brace at position 6.", 
   errorMsg);
  }
}
 
源代码29 项目: cacheonix-core   文件: OptionConverterTestCase.java
public
void varSubstTest5() {
  Properties props = new Properties();
  props.put("p1", "x1");
  props.put("p2", "${p1}");
  String res = OptionConverter.substVars("${p2}", props);
  System.out.println("Result is ["+res+"].");
  assertEquals(res, "x1");
}
 
源代码30 项目: cacheonix-core   文件: DailyFileAppender1.java
/**
   Retuns the option names for this component, namely {@link
   #FILE_NAME_PATTERN_OPTION} in
   addition to the options of {@link FileAppender#getOptionStrings
   FileAppender}.
*/
public
String[] getOptionStrings() {

  return OptionConverter.concatanateArrays(super.getOptionStrings(),
 new String[] {FILE_NAME_PATTERN_OPTION});
}
 
 类所在包
 同包方法