Git Product home page Git Product logo

flogger's Introduction

Flogger: A Fluent Logging API for Java

Maven Central Javadocs CI

What is it?

Flogger is a fluent logging API for Java. It supports a wide variety of features, and has many benefits over existing logging APIs.

Come for more self-documenting log statements:

logger.atInfo().withCause(exception).log("Log message with: %s", argument);

Stay for additional features that help you manage your logging better:

logger.atSevere()
    .atMostEvery(30, SECONDS)
    .log("Value: %s", lazy(() -> doExpensiveCalculation()));

Benefits

While some users prefer "fluency" as a style, this is not what the argument for Flogger rests on. Flogger offers these key, concrete advantages over other logging APIs:

  • Logging at disabled levels is effectively free. Finally, you can add as many fine-grained log statements to your code as you want, without worry.
  • Flogger also has very high performance for enabled log statements.
  • A fluent API accommodates a variety of present and future features without combinatorial explosion, and without requiring separate logging façades.
  • Less reliance on long parameter lists makes it harder to misuse and yields more self-documenting code.

Yet another logging API?

The field of open-source Java logging APIs is already extremely crowded, so why add another?

To paraphrase Douglas Adams "Google's codebase is big. Really big. You just won’t believe how vastly hugely mind-bogglingly big it is". Inevitably this resulted in many different debug logging APIs being used throughout the Java codebase, each with its own benefits and issues. Developers were forced to switch between APIs as they worked on different projects, and differences between APIs caused confusion and bugs.

Flogger is the result of an attempt to create a unified logging API, suitable for the vast majority of Java projects in Google.

For something of this magnitude it would have been preferable to use an existing logging API, rather than creating and maintaining our own. However, the Java Core Libraries Team (i.e. Guava maintainers) concluded that Flogger was not slightly better than the alternatives, but much better.

By switching the majority of Java code in Google to use Flogger, many thousands of bugs have been fixed and the cost to developers of learning new logging APIs as they move through the codebase has been eliminated. Flogger is now the sole recommended Java logging API within Google.

How to use Flogger

1. Add the dependencies on Flogger

All code that uses flogger should depend on com.google.flogger:flogger:<version> and com.google.flogger:flogger-system-backend:<version>.

Note: the dependency on flogger-system-backend is only required to be included when the binary is run. If you have a modularized build, you can include this dependency by the root module that builds your app/binary, and can be runtime scope.

2. Add an import for FluentLogger

import com.google.common.flogger.FluentLogger;

3. Create a private static final instance

private static final FluentLogger logger = FluentLogger.forEnclosingClass();

4. Start logging:

logger.atInfo().withCause(exception).log("Log message with: %s", argument);

Log messages can use any of Java's printf format specifiers; such as %s, %d, %016x etc.

Note that you may also see code and documentation that references the GoogleLogger class. This is a minor variant of the default FluentLogger designed for use in Google's codebase. The FluentLogger API is recommended for non-Google code, since its API should remain more stable over time.

More information

Flogger was designed and implemented by David Beaumont, with invaluable help from the Java Core Libraries Team and many other Googlers.

If you interested in a deeper dive into the rationale behind Flogger's API, please see Anatomy of an API.

flogger's People

Contributors

cgbatgoogle avatar cgdecker avatar chang-eric avatar chaoren avatar cpovirk avatar cushon avatar cydeweys avatar darthninja1 avatar dimo414 avatar donaldchai avatar dwoffinden avatar eamonnmcmanus avatar edwinkempin avatar emosenkis avatar graememorgan avatar hagbard avatar icbaker avatar iirina avatar java-team-github-bot avatar kluever avatar lukesandberg avatar netdpb avatar nreid260 avatar paladox avatar robinp-tw avatar ronshapiro avatar sammaier avatar timvdlippe avatar vladmos avatar xgnehz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flogger's Issues

Logging at disabled level is far from free

I've written a simple JMH test that test log4j, logback, java.util.logging ang flogger (with the system backend).

When the logging is disable, Flogger is faster than log4j and logback but slower than java.util.logging.
Did i have done something wrong ?

@CheckReturnValue on methods

image

This notification from IntelliJ leads me to think the LoggingApi methods themselves should be annotated with @CheckReturnValue instead of the LoggingApi class. But I would think IntelliJ/Android Studio should be smart enough to know a void method has no return value and that it should not warn in that case.

Are there no other backends ?

At first glance, this looked like an exciting new library. I really like the approaches to both API and performance concerns. But upon closer inspection, it looks like there are literally no backends supported other than to the ancient and long since totally abandoned Log4J project (not even Log4J 2). I am surprised that none of the current generation backends are supported, nor a general purpose framework like SLF4J. Is this library designed to only be used with legacy projects, or am I simply missing documentation on how to use other backends ?

ps.: I saw there is a logback backend project, but it's also been abandoned and is not available on Nexus, so I'm still very confused as to the status of this project in regards to projects with modern logging backends.

Flogger implementation should not read the configuration twice

I think there is an issue in your implementation when the configuration is changed dynamically. Your implementation read the configuration twice, once in at(Level) and once later in log() so the logger can check the level of one configuration and log using another newly updated configuration.

To fix this issue, you can read the configuration once in at() and propagate the configuration as a parameter of the Context.

Add log4j2 backend

Given that log4j backend was added recently (#4) it should be straight forward to add log4j2 backend.

Missing documentation for Metadata/Tags

While writing a backend for SLF4J, I stumbled across the Flogger concept of Metadata and Tags, which seem like they would map very well to SLF4J's concept of MDC. However, while I can see that one receives the Metadata and Tags in various places, my search has not turned up any way to actually set them.

It would be very helpful if someone could drop a few lines of code showing how to set a simple Tag to point me in the right direction so I could implement the parsing/delegating of it in the SLF4J backend.

Findbugs annotations should have optional or provided dependency

Currently both flogger and flogger-system-backend have a default (compile) dependency on com.google.code.findbugs:jsr305 which means it is packaged with every application. It this not required as Findbugs annotations are not required to run flogger. A better solution would be to either declare the dependency as optional or change the scope to provided.

Consider excluding Guava dependency from log4j backend

Currently flogger-log4j-backend:jar:0.3.1 depends on guava:jar:25.0-jre.

Please consider removing the dependency because:

  • Many projects already depend on some Guava version and they might end up with some kind of conflict using flogger.
  • Having Guava adds ~2.6M to the build result.
  • As of now (latest commit in the public flogger repository is cd769d1) only ImmutableMap is being used from Guava (apart from the unit tests).

I am wondering what are the good arguments for the other point of view. If this dependency can and should be removed, I am happy to provide a pull request.

LogSite class missing in flogger-0.3.jar from Maven Central

The LogSite class and related class are missing from the jar on Maven Central. This result in the following exception during runtime:

java.lang.ExceptionInInitializerError
	at com.google.common.flogger.backend.Platform.getCallerFinder(Platform.java:142)
	at com.google.common.flogger.FluentLogger.forEnclosingClass(FluentLogger.java:70)
	at mypkg.MyClass.<clinit>(MyClass.java:17)
// SNIP
Caused by: java.lang.IllegalStateException: No logging platforms found:
com.google.common.flogger.backend.system.DefaultPlatform: java.lang.NoClassDefFoundError: com/google/common/flogger/LogSite
	at com.google.common.flogger.backend.Platform$LazyHolder.loadFirstAvailablePlatform(Platform.java:99)
	at com.google.common.flogger.backend.Platform$LazyHolder.<clinit>(Platform.java:67)
	... 107 more

I don't know much about Bazel but I guess it has something to do with the new log_site target in api/BUILD. From the perspective of a Maven user the 0.3 release looks broken.

Bobbin backend support

Dear Team,

Please take a look at Bobbin logger: https://github.com/INFINITE-TECHNOLOGY/BOBBIN

Can you consider to adapt it as one of supported Flogger backends?

It features high performance and effective configuration.

Currently there are no known issues in Bobbin, it is a stable release.

If needed we can do appropriate changes (if any) in Bobbin.

Philosophically our vision is very close to that of Flogger, e.g. passing Objects to logger rather than Strings, etc.

Thank you.

Add build instructions

Bazel being less ubiquitous than Maven and Gradle, it'd be nice if there were some build instructions in the CONTRIBUTING.md page

Flogger Log4J backend causes Gradle 5.6 Sync to fail

Adding the Log4J backend causes an internal Gradle exception:

<ij_msg_gr>Project resolve errors<ij_msg_gr><ij_nav>A:\Tobi\Workspaces\JamesBot\build.gradle<ij_nav><i><b>root project 'james': Unable to resolve additional project configuration.</b><eol>Details: org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':runtimeClasspath'.<eol>Caused by: org.gradle.internal.resolve.ArtifactNotFoundException: Could not find jmxtools.jar (com.sun.jdmk:jmxtools:1.2.1).<eol>Searched in the following locations:<eol>    https://jcenter.bintray.com/com/sun/jdmk/jmxtools/1.2.1/jmxtools-1.2.1.jar</i>

CONFIGURE SUCCESSFUL in 0s

The faulty dependencies:

    compileOnly 'com.google.flogger:flogger:0.4'
    runtimeOnly 'com.google.flogger:flogger-log4j-backend:0.4'

Add extension to log() for inline return-statement

Coming from SLF4J/Logback, I am now lokking into flogger and log4j2 for future work...

While log4j2 is quite bulky and I like flogger's modern design, I was wondering if you'd be interested in an extension to log which returns the logged value? In log4j2, ther is a method called traceExit(T):T which does exactly that. For flogger I'd imagine something like returning(T):T which just calls log(T) and returns T, maybe with an optional text but not too much stuff. ;-)

return log4j2.traceExit("foo");

Exit with(foo)

Additionally, log4j provides the tracing of an entry-message to have a handle for the tracing of exit to be clear of input -> output... also a nice idea...

EntryMessage entryMessage = log4j2.traceEntry("entry-msg {}", "some-param");
// ...
return log4j2.traceExit(entryMessage, "result"); 

Enter entry-msg some-param
...
Exit entry-msg some-param: result

What Du you think about that? Would that fit into flogger? For example like that:

<T> T returning(T val){
    log("Returning: %s", val);
    return val;
}

<T> T returning(T val, String message){
    log("Returning [%s]: %s", message, val);
    return val;
}

Documentation for flogger libraries

Thanks for publishing the flogger libraries on Maven central
https://search.maven.org/#search%7Cga%7C1%7Ccom.google.flogger

Do you have documentation on which of these libraries contain which functionality ?

We are considering to migrate JGit from using slf4j API to flogger API and replace current usage
of log4 backend by using a flogger logging backend. I am not sure which of the flogger binaries
I would need to add as a dependency to JGit in order to achieve this goal.

The package javadocs are currently very generic and don't really help to answer this question.

-Matthias

bazel sync - throws "no such package '@google_bazel_common//'"

flogger git:(master) ✗ bazel sync
INFO: Invocation ID: b82bf732-b778-417e-96ed-7fc4713e7191
ERROR: error loading package '': Encountered error while reading extension file 'workspace_defs.bzl': no such package '@google_bazel_common//': The native http_archive rule is deprecated. load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") for a drop-in replacement.
Use --incompatible_remove_native_http_archive=false to temporarily continue using the native rule.
Building: no action
    Fetching @google_bazel_common; fetching

Publish to Maven Central

...so that flogger can be used by, well, everybody outside Google.

If flogger is already published to another public Maven repository, then please add some documentation on how to consume it.

Add support for custom level on forced

According to FluentLogger.java (flogger v0.4)
public Api at(Level level) {
boolean isLoggable = isLoggable(level);
boolean isForced = Platform.shouldForceLogging(getName(), level, isLoggable);
return (isLoggable || isForced) ? new Context(level, isForced) : NO_OP;
}
when isForced true, if we could customize level, e.g. -Dflogger_level_on_forced=FINE, then more log will be outputted for debugging.

Flogger pattern for multiple loggers in a class

Is there an idiomatic pattern for migrating to Flogger when using multiple loggers in a class?
Example:

private final Logger logger = LoggerFactory.getLogger( getClass() );
private final Logger someOtherLogger = LoggerFactory.getLogger( "someOtherLoggerName" );

A variation on that would be how to use non-class-named Flogger loggers, as the only out-of-the-box mechanism is based on the class (from FluentLogger):

  public static FluentLogger forEnclosingClass() {
    // NOTE: It is _vital_ that the call to "caller finder" is made directly inside the static
    // factory method. See getCallerFinder() for more information.
    String loggingClass = Platform.getCallerFinder().findLoggingClass(FluentLogger.class);
    return new FluentLogger(Platform.getBackend(loggingClass));
  }

how to customize the context

Hi,

I'm new to flogger. Thanks for this wonderful fluent logger.

Currently, I use atMostEvery() often by calling logger.at(level).atMostEvery(time, TimeUnit.MILLISECONDS).log(message);

Is there's a way to customize the following context statement that are being appended to every log statement ?

[CONTEXT ratelimit_period="2000 MILLISECONDS [skipped: 2]" ] is a bit too verbose to my taste.

I'd like to shrink it down to just [2000,2].

I hope I'm making a very reasonable request here :D

Thanks for any help!

Improve documentation with configuration informaton

From the existing documentation it is not clear to me if flogger is intended mostly as a replacement for the logging api (slf4j) or also for the logging backend (logback/log4j), or both.

The documentation focuses quite convincingly on why this is a better API, but does not seem to give any information about how to configure the backend (log appenders, formatting, MDC etc.). It also doesn't mention anything about whether it's recommended to use an alternate backend, and if so, how it would be accomplished.

Perhaps I've missed something, but could you possibly either point me to the existing docs i missed, or clarify some of the above? Thanks much!

Was the flogger name intentional?

Thought I'd bring this to your attention, but flogger is not an original word. I don't think it was intentional, but I remembered seeing the word used in exchange with "drawing and quartering" which is the act of corporal punishment. A simple google search of "flogger" definitely raised an eyebrow.

See flagellation and the urban dictionary for flogger. Note the definition may be NSFW.

I think it was just meant to be a combination of "fluent + logger" and the result was unintended. Anyways, feel free to close this.

Configurable LogCallerFinder via system properties and other improvements

Using DefaultPlatform one can configure BackendFactory, LoggingContext, and Clock via system properties, however LogCallerFinder remains set as StackBasedCallerFinder with no option to change that. I would like the ability added to configure the LogCallerFinder as well. This is certainly useful when one is writing custom backends, especially when it comes to those where naming your loggers using the stack doesn't make much sense. The property name could be flogger.caller_finder or something of the sort.

Also it would be nice to set the resolveAttribute method in DefaultPlatform to public as the functionality of dynamic configuration and extensibility via system properties seems quite useful and I find that I would like to utilize it for consistency and simplicity when writing dynamically extensible Flogger backends. An example of a useful case for this is where the library you are writing the backend for does not support disabling Levels and you want to make that functionality available through dynamically configured extensions instead of providing a single prescribed way.

I am currently resorting to some less than preferable reflection tricks when doing all of the above in my Fluentd backend as seen here:
https://github.com/agsimeonov/flogger-fluentd-backend/blob/master/src/main/java/com/agsimeonov/flogger/backend/fluentd/FluentdBackendFactory.java

The only other option I see is writing my own Platform but then I would have to resort to classpath tricks to provide it through a custom PlatformProvider which also doesn't seem like a particularly pleasant option. I would also have to rewrite a lot of perfectly good code already present in DefaultPlatform simply to add a tiny bit of extensibility.

Last but not least, and maybe I need to create a separate issue for this as it is not related to the above, I would like to talk about Metadata and SimpleMessageFormatter. The code here https://github.com/google/flogger/blob/master/api/src/main/java/com/google/common/flogger/backend/SimpleMessageFormatter.java#L85 appends what is deemed unknown metadata to the log message and there is no way of doing anything fancier with this unknown metadata instead. I propose creating a handler for it that could be used to extend what can be done with it. This is now becoming especially useful when we consider that the with() functionality GoogleLogger provided is now merged into FluentLogger. More people are certainly starting to use Metadata so there needs to be a better way to handle the formatting of this Metadata. Now I know one can simply create their own MessageBuilder however SimpleMessageFormatter does a lot of things great and one might want to expand upon it rather than completely rewrite it.

If all of or at least a part of all of this seems reasonable I can certainly contribute with a pull request. Let me know what you guys think. I love what you have done with Flogger and I am certainly open to other ideas/suggestions as well. Keep it up!

How to add custom logger back-end?

It looks like PlatformProvider may be the mechanism for this, though it could be argued it's responsibility is deciding between Android or 'Default' platforms.

Adding a JAR with a custom PlatformProvider does not work (in all cases) as PlatformProvider is included in the base flogger JAR (it may work if at the right spot in the classpath - quirky, not to be relied on).

Improve documentation for slf4j usage

Maybe I missed it, but had rally hard time integrating slf4js. I added correct dependencies, but default logger factory was used.

After debugging and diggind through source, I found out that I need to add system property:
-Dflogger.backend_factory=com.google.common.flogger.backend.slf4j.Slf4jBackendFactory#getInstance

I have not found mention of this anywhere. I would be nice to have it mentioned in the docs.

Simplify Log4j backend configuration

In contrast to Slf4j it's harder to configure Log4j as a backend for Flogger.

For Slf4j just log4j-slf4j-impl jar should be in class path, but for Flogger both flogger-system-backend and flogger-log4j-backend should be on class path and additionally flogger.backend_factory property should be set.

Need to include flogger-system-backend appears illogical if user wants to use Log4j backend and setting system property requires additional work comparing to just having log4j-slf4j-impl in class path.

Release 0.5

Hi, could a 0.5 release be done please? It includes log4j2 support.

Add log4j backend

We want to use Flogger in Gerrit where we currently use slf4j with a log4j backend. We want to continue using log4j as backend so that the switch to Flogger is transparent for existing Gerrit installations and existing log4j configurations continue to work.

Not able to use slf4j-backend

Using these dependencies:

<dependency>
    <groupId>com.google.flogger</groupId>
    <artifactId>flogger</artifactId>
    <version>0.4</version>
</dependency>
<dependency>
    <groupId>com.google.flogger</groupId>
    <artifactId>flogger-slf4j-backend</artifactId>
    <version>0.4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.2</version>
</dependency>

should be enough if I understand https://github.com/google/flogger#how-to-use-flogger correctly.

But this test:

public class FloggerTest {

    private static final FluentLogger log = FluentLogger.forEnclosingClass();

    @Test
    public void test() {
        log.atWarning().log("Test: %s", "message");
    }
}

fails with:

java.lang.ExceptionInInitializerError
	at com.google.common.flogger.backend.Platform.getCallerFinder(Platform.java:142)
	at com.google.common.flogger.FluentLogger.forEnclosingClass(FluentLogger.java:70)
	at test.FloggerTest.<clinit>(FloggerTest.java:10)
Caused by: java.lang.IllegalStateException: No logging platforms found:
com.google.common.flogger.backend.system.DefaultPlatform: java.lang.ClassNotFoundException: com.google.common.flogger.backend.system.DefaultPlatform
	at com.google.common.flogger.backend.Platform$LazyHolder.loadFirstAvailablePlatform(Platform.java:99)
	at com.google.common.flogger.backend.Platform$LazyHolder.<clinit>(Platform.java:67)
	... 25 more

According to https://github.com/google/flogger/blob/master/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jBackendFactory.java#L30 you have to set the flogger.backend_factory system property, so I started Java with -Dflogger.backend_factory=com.google.common.flogger.backend.slf4j.Slf4jBackendFactory#getInstance, but this resulted in the same exception.

What am I missing?

(the whole project can be found at: https://github.com/PascalSchumacher/flogger-slf4j-test)

bazel build @flogger//google:flogger is broken at head

When using this repo as a Bazel external dependency:

git_repository(
    name = "google_bazel_common",
    commit = "fe3a28b3fd66bd6d0e1b6736efd886056cd1c8cc", # head
    remote = "https://github.com/google/bazel-common.git",
)

git_repository(
    name = "flogger",
    remote = "https://github.com/google/flogger.git",
    tag = "flogger-0.2",
)

Attempting to build the main artifact:

$ bazel build @flogger//google:flogger
ERROR: /private/var/tmp/_bazel_blah/7a13dd7a63acf54d11ec8dedf378ff73/external/google_bazel_common/testing/test_defs.bzl:87:9: name 'android_library' is not defined
ERROR: Skipping '@flogger//google:flogger': error loading package '@flogger//google': Extension 'testing/test_defs.bzl' has errors

The cause is clear. @flogger//google/BUILD loads the test_defs.bzl file from bazel-common, and that file does not parse. The references to android_library and android_local_test here need to be prefixed with native.

(I'm filing this with Flogger rather than bazel-common because bazel build @google_bazel_common//... actually works. The repo doesn't import the broken file anywhere.)

Missing android package?

Hi, I tried to integrate flogger into a Liferay OSGi module but I get an error at deploy time:
Unresolved requirement: Import-Package: com.google.common.flogger.backend.android

I see a reference to this package (using Github search) here:
https://github.com/google/flogger/blob/master/api/proguard.cfg
here:
https://github.com/google/flogger/blob/master/api/src/main/java/com/google/common/flogger/backend/Platform.java
and here:
https://github.com/google/flogger/blob/1e2caa0b5b46b79377c5778be42b63f2ebc0655c/api/platformprovider/main/java/com/google/common/flogger/backend/PlatformProviderGenerator.java

but it doesn't seem to exist
https://github.com/google/flogger/tree/master/api/src/test/java/com/google/common/flogger/backend

Should it work without it?

ClassNotFoundException: com.google.common.flogger.backend.system.DefaultPlatform

jdk1.8.0_181.jdk
    <dependency>
      <groupId>com.google.flogger</groupId>
      <artifactId>flogger</artifactId>
      <version>0.3.1</version>
    </dependency>
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.google.common.flogger.backend.Platform.getCallerFinder(Platform.java:142)
	at com.google.common.flogger.FluentLogger.forEnclosingClass(FluentLogger.java:70)
	at io.tapwater.server.GrpcServer.<clinit>(GrpcServer.java:13)
Caused by: java.lang.IllegalStateException: No logging platforms found:
com.google.common.flogger.backend.system.DefaultPlatform: java.lang.ClassNotFoundException: com.google.common.flogger.backend.system.DefaultPlatform
	at com.google.common.flogger.backend.Platform$LazyHolder.loadFirstAvailablePlatform(Platform.java:99)
	at com.google.common.flogger.backend.Platform$LazyHolder.<clinit>(Platform.java:67)
	... 3 more

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.